Android TabLayout inside Fragment

大兔子大兔子 提交于 2019-11-27 19:07:33

After looking into yours code, I found that you forget to return a fragment in getItem method of PagerAdapter that you overrided from the FragmentStatePagerAdapter and there is no break statement in each case due to which every time a null value was returned.

To doing so you need to create a another new fragment for each page in ViewPager or you can create one new fragment that could use in all page of ViewPager.

You can't use the same recyclerview into the tabs of view pager that you have defined in parent fragment.

activity_fragment_one.xml

 <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".NewFragment">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            app:tabTextAppearance="?android:attr/textAppearanceMedium"
            app:tabTextColor="#ffffff"
            android:layout_height="?android:actionBarSize"
            app:tabMode="fixed"
            app:tabGravity="fill"/>

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:background="@android:color/white"/>
</android.support.design.widget.CoordinatorLayout>

I have fixed your fragment and created a new Fragment that you can use in ViewPager Pages.

public class NewFragment extends Fragment {

private RecyclerView mRecyclerView;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.activity_fragment_one, container, false);
    TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tabs);
    tabLayout.addTab(tabLayout.newTab().setText("A"));
    tabLayout.addTab(tabLayout.newTab().setText("B"));
    tabLayout.addTab(tabLayout.newTab().setText("C"));
    final ViewPager viewPager = (ViewPager) view.findViewById(R.id.viewpager);
   // mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
    LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
    mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    //mRecyclerView.setLayoutManager(mLayoutManager);
    viewPager.setAdapter(new PagerAdapter(getFragmentManager(), tabLayout.getTabCount()));
    viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
    tabLayout.setupWithViewPager(viewPager);
    tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
    tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {

        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            viewPager.setCurrentItem(tab.getPosition());
        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {

        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {

        }
    });

    return view;
}

public class PagerAdapter extends FragmentStatePagerAdapter {
    int mNumOfTabs;

    public PagerAdapter(FragmentManager fm, int NumOfTabs) {
        super(fm);
        this.mNumOfTabs = NumOfTabs;
    }


    @Override
    public Fragment getItem(int position) {

        switch (position) {
            case 0:
              return new FragmentTab();
            case 1:
                return new FragmentTab();
            case 2:
                return new FragmentTab();

            default:
                return null;
        }
    }

    @Override
    public int getCount() {
        return mNumOfTabs;
    }
}
}

FragmentTab for each Page in View Pager (or you can create different fragment for each page according to yours need).

public class FragmentTab extends Fragment {
RecyclerView mRecyclerView;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_tab, container, false);

    mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
    LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
    mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    mRecyclerView.setLayoutManager(mLayoutManager);
    RecyclerView.Adapter mAdapter = new AdapterOne();
    mRecyclerView.setAdapter(mAdapter);
    return view;
}
}

fragment_tab.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />


</LinearLayout>
madhu sudhan

Use getChildFragmentManager() instead of getSupportFragmentManager()

Without the error logs, it's difficult to say where and why do you get the NullPointerException.

The first thing that caught my attention is that you didn't link your TabLayout and ViewPager. Use the following code after you set the adapter for the ViewPager:

// setUpWithViewPager doesn't work without using a Runnable interface.
// Support library bug, maybe?
tabLayout.post(new Runnable() {
    @Override
    public void run() {
        tabLayout.setupWithViewPager(viewPager);
    }
});

I'm not sure if this is the problem, but give it a try :)

Well, some time ago I implemented exactly the same app design. I had a fragment, that intended to hold Tabs and ViewPager. That was a tricky task, and I recall that I faced similar problems as you did. Here is how I solved it:

The layout sequence is:

Activity -> Fragment -> Tab Layout + ViewPager -> FragmentStatePagerAdapter -> Fragment -> RecyclerView

You've got nothing to do with your Main Activity. All the stuff is in fragments. So get rid from TabLayout in your activity_main.xml.

You will have to deal with nested fragments inside pager. All page content must be there and TabLayout must be in the same layout where you ViewPager is.

My project is a bit old, and I used android.support.v4.app.Fragment in case if it makes sense. I stripped sources from data you wont need, so you may see refers to some orphaned methods.

The task was to display price list with categories, and render each category products on its own page, so that horizontal swipes would switch categories and vertical swipes — list products.

Each fragment with ViewPager and TabLayout has following subfragment_price_list_pager.xml layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/icons"
    app:cardCornerRadius="4dp"
    app:cardElevation="4dp"
    app:cardPreventCornerOverlap="true">

    <android.support.v7.widget.Toolbar
        android:id="@+id/priceListToolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:elevation="4dp"
        android:theme="@style/AppTheme.Toolbar.TabLayout"
        app:navigationIcon="@drawable/ic_tag_multiple_grey600_24dp"
        tools:ignore="UnusedAttribute">

        <android.support.design.widget.TabLayout
            android:id="@+id/tabBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true" />
    </android.support.v7.widget.Toolbar>

    <android.support.v4.view.ViewPager
        android:id="@+id/priceList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="58dp"
        android:padding="5dp"
        android:scrollbars="horizontal" />


</android.support.v7.widget.CardView>

Here is the PriceListPagerFragment.class:

public class PriceListPagerFragment extends Fragment {
    private static final String TAG = "PriceListPagerFragment";
    private PriceList mPriceList;

    private ViewPager mCategoriesViewPager;
    private TabLayout tabLayout;

    private Loader<PriceList> priceListLoader;
    private EditText scanerBarcodeText;
    private OnCartActionListener mListener;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.subfragment_price_list_pager, container, false);

        // Pager that would be inflated with page fragments.
        mCategoriesViewPager = (ViewPager) rootView.findViewById(R.id.priceList);
        tabLayout = (TabLayout) rootView.findViewById(R.id.tabBar);
        tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);

        // Do all the stuff with pager here:
        updatePriceListPages();

        return rootView;
    }


    private void updatePriceListPages() {
        if (mCategoriesViewPager != null && mPriceList != null) {
            Log.d(TAG, this + "updatePriceListPages");
            if (mCategoriesViewPager.getAdapter() == null) {


                PriceListCategoriesAdapter adapter = new PriceListCategoriesAdapter(
                        getFragmentManager(),
                        tabLayout
                );
                mCategoriesViewPager.setAdapter(
                        adapter
                );
                // Each page is a content of a single category of one-level catalog 
                // We did not supply catalog to adapter before it is bound to ViewPager for some serious reason which I can't recall now 
                adapter.setPriceList(mPriceList);

                tabLayout.setupWithViewPager(mCategoriesViewPager);
            } else {
                ((PriceListCategoriesAdapter) mCategoriesViewPager.getAdapter()).setPriceList(mPriceList);
            }
        }
    }
}

Custom FragmentStatePagerAdapter implementation, PriceListCategoriesAdapter.class:

public class PriceListCategoriesAdapter extends FragmentStatePagerAdapter {
    private static final String TAG = "PriceListCategoriesAd";
    private final TabLayout mTabLayout;
    private FragmentManager mFm;
    private Vector<PriceListFragment> recyclerViewList;
    private Vector<String> titles;

    public PriceListCategoriesAdapter(FragmentManager fm, TabLayout tabLayout) {
        super(fm);
        mFm = fm;
        mTabLayout = tabLayout;
        recyclerViewList = new Vector<>();
        titles = new Vector<>();
    }


    // All the magic with nested page fragments is done here
    public void setPriceList(PriceList priceList) {
        boolean updateTabs = false;

        Vector<String> newTitles = new Vector<>();

        int position = 0;
        for (CatalogItem catalogItem : priceList.getCatalogs()) {

            if (catalogItem.getProducts() == null)
                continue;

            boolean newFragment;

            PriceListFragment fragment;
            try {
                fragment = recyclerViewList.get(position);
                newFragment = false;

            } catch (ArrayIndexOutOfBoundsException e) {
                fragment = new PriceListFragment();
                newFragment = true;
            }

            fragment.setCatalogItem(catalogItem);

            newTitles.add(catalogItem.getName());

            if (newFragment) {
                recyclerViewList.add(fragment);
            }
            position++;
        }

        if (titles.size() != newTitles.size()) {
            updateTabs = true;
        } else {
            for (int position = 0; position < titles.size(); position++) {
                if (!titles.get(position).equals(newTitles.get(position))) {
                    updateTabs = true;
                    break;
                }
            }
        }

        titles = newTitles;
        notifyDataSetChanged();

        if (updateTabs)
            mTabLayout.setTabsFromPagerAdapter(this);
    }


    @Override
    public Fragment getItem(int position) {
        return recyclerViewList.get(position);
    }

    @Override
    public int getItemPosition(Object object) {
        PriceListFragment fragment = (PriceListFragment) object;
        String title = (String) fragment.getTitle();
        int position = titles.indexOf(title);

        if (position >= 0) {
            return position;
        } else {
            return POSITION_NONE;
        }
    }

    @Override
    public int getCount() {
        return titles.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return recyclerViewList.get(position).getTitle();
    }

    // We don't keep states
    @Override
    public Parcelable saveState() {
        return null;
    }
    // We don't keep states
    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
    }
}

And finally PriceListFragment layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView android:id="@+id/categoryList"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical" />

PriceListFragment.class itself has nothing special. Just instantiate it's RecyclerView with proper data.

masoud vali

i suggest using getChildFragmentManager instead of getFragmentManager in

viewPager.setAdapter(new PagerAdapter (getFragmentManager(), tabLayout.getTabCount()));

Phuoc Huynh

This method getItem in PagerAdapter wrong, it return Fragment init You should return Fragment. And in fragment, you setView recyclerView to it

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!