Jimit Patel
Jimit Patel

Reputation: 4297

Viewpager with different menu and common toolbar not working

I have tabs in my app. Each tab is of different fragments and has different menu. Below is the layout which I am using

    <?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/background">

            <include
                layout="@layout/toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_scrollFlags="scroll|enterAlways" />


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

        <com.CustomViewPager
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:background="@color/background"
            app:layout_anchor="@id/view_pager"
            app:layout_anchorGravity="bottom|center_horizontal"
            app:layout_behavior="com.widget.ScrollTabBehavior"
            app:tabBackground="@color/background"
            app:tabIndicatorColor="@color/toolbar"
            app:tabIndicatorHeight="0dp"
            app:tabMode="fixed"
            app:tabPaddingEnd="0dp"
            app:tabPaddingStart="0dp" />

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

    <RelativeLayout
        android:id="@+id/left_drawer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:layout_marginLeft="-64dp"
        android:layout_marginStart="-64dp"
        android:background="@color/toolbar"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp">

        <ImageButton
            android:id="@+id/close_btn"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:background="@android:color/transparent"
            android:padding="5dp"
            android:src="@drawable/close_icon" />

        <view
            android:id="@+id/drawerlist"
            class="android.support.v7.widget.RecyclerView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/close_btn" />

    </RelativeLayout>
</android.support.v4.widget.DrawerLayout>

Now with every Fragment in onCreate() I have mentioned setHasOptionsMenu(true); further more. I have overriden the function onCreateOptionsMenu() in each fragment which has menu.clear(); at first then calling it's super constructor then inflating fragment's own menu xml. But result I am getting is something like this -

In short, according to my observation it is showing the menu of adjacent tabs which is actually getting executed after the current fragment and therefore such behavior is occurring.

So how to avoid this?

Upvotes: 5

Views: 8588

Answers (1)

Konstantin Loginov
Konstantin Loginov

Reputation: 16000

I have written small test-application to check the behaviour.

enter image description here

Let's go through the sample and see, if something wrong with your fragments (as you see above, ViewPager with different menus works like a charm)

Activity's XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_below="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

Activity class. Important part is invalidateOptionsMenu() every time ViewPager has PageSelected event. Then, we setting setHasOptionsMenu to all fragments and subfragments(from the nested ViewPagers) to false if they out of screen.

public class MainActivity extends AppCompatActivity {

    PagerAdapter pagerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Fragment[] fragments = {
                Fragment.instantiate(this, FragmentNoMenu.class.getName()),
                Fragment.instantiate(this, FragmentA.class.getName()),
                Fragment.instantiate(this, FragmentNoMenu.class.getName()),
                Fragment.instantiate(this, FragmentB.class.getName()),
        };

        TabLayout tabLayout = (TabLayout)findViewById(R.id.tabLayout);
        ViewPager viewPager = (ViewPager)findViewById(R.id.viewPager);
        pagerAdapter = new PagerAdapter(getSupportFragmentManager(), fragments);
        viewPager.setAdapter(pagerAdapter);
        viewPager.setOffscreenPageLimit(0);
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
            @Override
            public void onPageSelected(int position) {
                invalidateOptionsMenu(position);
            }
            @Override
            public void onPageScrollStateChanged(int state) {}
        });

        invalidateOptionsMenu(0);
        tabLayout.setupWithViewPager(viewPager);
    }

    private void invalidateOptionsMenu(int position) {
        for(int i = 0; i < pagerAdapter.getCount(); i++) {
            Fragment fragment = pagerAdapter.getItem(i);
            fragment.setHasOptionsMenu(i == position);

            if (fragment instanceof FragmentWithViewPager) {
                FragmentWithViewPager fragmentWithViewPager = (FragmentWithViewPager)fragment;
                if (fragmentWithViewPager.pagerAdapter != null) {
                    for (int j = 0; j < fragmentWithViewPager.pagerAdapter.getCount(); j++) {
                        fragmentWithViewPager.pagerAdapter.getItem(j).setHasOptionsMenu(i == position);
                    }
                }
            }
        }

        invalidateOptionsMenu();
    }
}

PagerAdapter class:

public class PagerAdapter extends FragmentPagerAdapter {

    private final Fragment[] fragments;

    public PagerAdapter(FragmentManager fragmentManager, Fragment[] fragments) {
        super(fragmentManager);
        this.fragments = fragments;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return fragments[position].getClass().getSimpleName();
    }

    @Override
    public Fragment getItem(int position) {
        return fragments[position];
    }

    @Override
    public int getCount() {
        return fragments.length;
    }
}

Here're my test fragments I used:

FragmentNoMenu class:

public class FragmentNoMenu extends android.support.v4.app.Fragment {

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

FragmentNoMenu layout:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#0F0F50"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

FragmentA class is a Fragment with nested ViewPager:

public class FragmentA extends FragmentWithViewPager {

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

        Fragment[] fragments = {
                Fragment.instantiate(getContext(), SubFragmentA.class.getName()),
                Fragment.instantiate(getContext(), SubFragmentB.class.getName()),
                Fragment.instantiate(getContext(), SubFragmentC.class.getName()),
        };

        if (pagerAdapter == null) {
            pagerAdapter = new PagerAdapter(getChildFragmentManager(), fragments);
        }

        viewPager = (ViewPager)rootView.findViewById(R.id.viewPager);
        viewPager.setAdapter(pagerAdapter);
        return rootView;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.fragment_a, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }
}

FragmentA's layout:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#B0B0B0"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_marginTop="80dp"
        android:layout_width="match_parent"
        android:layout_height="200dp"/>
</FrameLayout>

FragmentA's menu:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_item_1"
        android:title="Item 1"
        android:orderInCategory="250"
        app:showAsAction="never" />

    <item
        android:id="@+id/action_item_2"
        android:title="Item 2"
        android:orderInCategory="300"
        app:showAsAction="never" />
</menu>

NB! FragmentA extends FragmentWithViewPager - it's a small extension of Fragment to make it easier to distinguish Fragments with nested fragments in MainActivity:

public class FragmentWithViewPager extends Fragment {
    PagerAdapter pagerAdapter;
    ViewPager viewPager;
}

FragmentB:

public class FragmentB extends Fragment {

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

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.fragment_b, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }
}

It's layout & menu:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#999999"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

.....

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_item3"
        android:title="Item 3"
        android:icon="@drawable/ic_triage_star"
        android:orderInCategory="250"
        app:showAsAction="always" />
</menu>

Subfragments (they are all look the same from the code perspective):

Layouts:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#AA0000"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:text="SubFragment C (with icon Menu)"
        android:textSize="24sp"
        android:textColor="#00BB00"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</FrameLayout>

Code:

public class SubFragmentB extends Fragment {

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

That's it! I've uploaded the project to my dropbox - feel free to check it out!

I hope, it helps

Upvotes: 18

Related Questions