daniel_c05
daniel_c05

Reputation: 11518

Implementing a TabListener using the Support Library

I am trying to implement Tab Navigation, but I want to make sure people that have older versions of Android can still use my application.

The app in mind ATM is fairly simple, I just want to be able to understand how to implement the layout and then I'll add the missing bits.

Anyhow, I have a Container Activity that extends Fragment Activity (to ensure compatibility), and this Activity creates a TabView using an ActionBar (I believe my problem resides here). The app will try to create three tabs and add them to the ActionBar, and I want to make sure the user can scroll back and forth using lateral navigation.

Here is TabListener I am trying to implement:

public static class TabListener<T extends Fragment> implements ActionBar.TabListener {

    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    public TabListener(Activity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        if (mFragment == null) {
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
        } else {
            ft.attach(mFragment);
        }
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            ft.detach(mFragment);
        }
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }
}

Here are my imports, because I wanted to make sure I was using the support library:

import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.view.Menu;

However, Eclipse is giving me issues with the TabListener methods. It is telling me the following: "The type LayoutContainer.TabListener must implement the inherited abstract method ActionBar.TabListener.onTabSelected(ActionBar.Tab, FragmentTransaction)"

When I select Add unimplemented methods Eclipse basically adds the OnTabSelected OnTabReselected and OnTabUnselected methods, but this time, passing the non-support version of the Fragment (android..app.Fragment) as a parameter.

Any ideas on how to make another implementation of lateral navigation through the support library to ensure compatibility?

Upvotes: 20

Views: 22377

Answers (5)

serv-inc
serv-inc

Reputation: 38177

Any ideas on how to make another implementation of lateral navigation through the support library to ensure compatibility?

Alternative solution

As of 29 May 2015, you can use the Android Design Support Library. It includes a Tab Layout and supports Android 2.1 or higher devices.

Upvotes: 0

John Bentley
John Bentley

Reputation: 1824

The key is to use

import android.support.v7.app.ActionBar;

... rather than ...

import android.app.ActionBar;

That avoids the clever workaround Nelson Ramirez posted.

The following full example, based on the official documentation, was tested to work from Android 3.0, API 11

package com.example.myapp;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.*;
import android.widget.TextView;

public class NavigationTabsBasicDemoActivity extends ActionBarActivity {

    static public class TabListener<T extends Fragment> implements ActionBar
            .TabListener {

        private Fragment mFragment;
        private final Activity mActivity;
        private final String mTag;
        private final Class<T> mClass;

        /**
         * Constructor used each time a new tab is created.
         *
         * @param activity The host Activity, used to instantiate the
         *                 fragment
         * @param tag      The identifier tag for the fragment
         * @param pClass   The fragment's Class, used to instantiate the
         *                 fragment
         * @see <a
         * href="http://developer.android.com/guide/topics/ui/actionbar
         * .html#Tabs">
         * Developers Guide > Action Bar > Adding Navigation Tabs</a>
         */
        public TabListener(Activity activity, String tag, Class<T> pClass) {
            mActivity = activity;
            mTag = tag;
            mClass = pClass;
        }

        @Override
        public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
            if (mFragment == null) {
                mFragment = Fragment.instantiate(mActivity, mClass.getName());
                ft.add(android.R.id.content, mFragment, mTag);
            } else {
                // If it exists, attach it in order to show it
                ft.attach(mFragment);
            }
        }

        @Override
        public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
            if (mFragment != null) {
                // Detach the fragment, because another one is about to be
                // attached.
                ft.detach(mFragment);
            }
        }

        @Override
        public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
            // Do nothing.
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // No need for setContentView() to be used, Instead  we use the root
        // android.R.id.content as the container for each fragment,
        // which is set in the TabListener

        ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        actionBar.setDisplayShowTitleEnabled(true);

        ActionBar.Tab tab = actionBar.newTab().setText("Artist").setTabListener(
                new TabListener<PlaceholderFragment>(this,
                                                     "artist",
                                                     PlaceholderFragment
                                                             .class));
        actionBar.addTab(tab);

        tab = actionBar.newTab().setText("Album").setTabListener(
                new TabListener<PlaceholderFragment>(
                    this,
                    "album",
                    PlaceholderFragment.class));
        actionBar.addTab(tab);
    }

    /**
     * In this example use one Fragment but display different data based on
     * which
     * tab is shown. In production you'd probably use a separate fragment.
     */
    public static class PlaceholderFragment extends Fragment {

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(
                    R.layout.fragment_navigation_tabs_basic_demo,
                                             container,
                                             false);
            TextView outputTextView = (TextView) rootView.findViewById(
                                        R.id.output_textView);
            outputTextView.setText("Hello " + getTag());
            return rootView;
        }
    }
}

Upvotes: 5

Anna Billstrom
Anna Billstrom

Reputation: 2492

I wanted to implement @nelson-ramirez but had an error accessing mActivity, so this is a combination of those two answers, and works for my project, which uses a tab navigation with Facebook login (which requires support.v4 library). the keys are, creating a private mActivity that you then pass in and assign when initiating the listener, and creating your own Fragment Transaction, not using the one from the argument. Also, change the main activity to FragmentActivity, using v4 library, which allows access to getSupportFragmentManager().

public class MyTabListener implements ActionBar.TabListener{
    private Fragment fragment;
    private FragmentActivity mActivity;

    public MyTabListener(Fragment fragment, FragmentActivity activity){
        this.fragment = fragment;
        this.mActivity = activity;
    }

    @Override
    public void onTabSelected(ActionBar.Tab tab, android.app.FragmentTransaction ft) {
        android.support.v4.app.FragmentTransaction fft = mActivity.getSupportFragmentManager().beginTransaction();
        fft.replace(R.id.fragment_container, fragment);
        fft.commit();
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab, android.app.FragmentTransaction ft) {
        android.support.v4.app.FragmentTransaction fft = mActivity.getSupportFragmentManager().beginTransaction();
        fft.remove(fragment);
    }

    @Override
    public void onTabReselected(ActionBar.Tab tab, android.app.FragmentTransaction ft) {

    }
}

Upvotes: 0

Nelson Ramirez
Nelson Ramirez

Reputation: 8004

hmmm. while Malek's works it doesn't directly answer the question..

You can simply ignore the fragment transaction you get in the callback and use your own:

android.support.v4.app.FragmentTransaction fft = mActivity.getSupportFragmentManager().beginTransaction();

Just make sure that your activity is a FragmentActivity and you'll be able to start a new fragment transaction.

Also the replace() method in the fragmentTransaction is much more convenient than add() and remove()

Upvotes: 33

Malek Hijazi
Malek Hijazi

Reputation: 4162

actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowTitleEnabled(false);

tabA = actionBar.newTab().setText("");
tabB = actionBar.newTab().setText("");

Fragment fragmentA = new AFragmentTab();
Fragment fragmentB = new BFragmentTab();

tabA.setTabListener(new MyTabsListener(fragmentA));
tabB.setTabListener(new MyTabsListener(fragmentB));

actionBar.addTab(tabA);
actionBar.addTab(tabB);

The tab listener is as follows:

protected class MyTabsListener implements ActionBar.TabListener{
    private Fragment fragment;

    public MyTabsListener(Fragment fragment){
        this.fragment = fragment;
    }
    public void onTabSelected(Tab tab, FragmentTransaction ft){
        ft.add(R.id.layout2, fragment, null);
    }
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        ft.remove(fragment);
    }
}

and then you make a class for each tab:

public class BFragmentTab extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.login, container, false);
    }
}

But keep in mind that the action bar isn't supported for android versions below 3.0 . If you want to use it in older versions I suggest you use actionBarSherlock Library.

Upvotes: 3

Related Questions