Android – viewpager has different menus and common toolbars and does not work properly
There are labels in my application. Each label has different fragments and different menus. Here is the layout I'm 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, each fragment in oncreate () refers to sethasoptionsmenu (true); More. I have overridden the oncreateoptionsmenu() function in each fragment containing menu. Clear(). First call its super constructor, and then expand the fragment's own menu XML. But the result I get is like this –
>Suppose there are five tabs. The viewpager in the second and third tabs each contains less than two clips > the first tab has no menu > the second tab has menu_ 2 (only applicable to the second sub segment) > the third tab has no menu again > the fourth tab has menu_ 4 (only applicable to the first sub segment) > the fifth label has menu_ 5 > initially, tab 1 should have no menu. Then move directly to the third tab, which displays menu_ 4. The menu is not displayed by default. Then slide to tab 4 and it will display the correct menu_ 4 then slide back to the third tab, which will show no menu (this is required). > the same will happen for tab 5. If I switch to the second sub segment in the second tab, the same behavior will appear in the first tab
In short, according to my observation, it shows the menu of adjacent labels, which is actually executed after the current fragment, so this behavior is happening
So how to avoid this situation?
resolvent:
I wrote a small test application to check behavior
Let's take a look at the sample to see if there is a problem with your clip (as shown above, viewpager and different menus are like charm)
Active 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. When every viewpager has a pageselected event, the important part is invalideoptionsmenu(). Then, we set sethasoptionsmenu to false for all fragments and sub fragments (from nested viewpagers) if they are off 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;
}
}
This is the test fragment 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"/>
The fragmenta class is a fragment of a 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 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 – a small extension of fragment that makes it easier to distinguish fragments from 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);
}
}
Invalid Sign
<?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>
Sub fragments (they all look the same from a code point of view):
Layout:
<?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);
}
}
nothing more! I have uploaded the project to my Drop@R_ 572_ 2419@ – feel free to check it out!
I hope it helps