Android onOptionsItemSelected in fragment is not triggered for android OS below 5 - android

I have an android.support.v4.app.Fragment in an AppCompatActivity.
I added a custom Menu in my fragment. Everything works as expected in Android 5 and 6. But if I try this on Android 4, although I can see the toolbar, when I tap the menu items nothing happens and it does not trigger onOptionsItemSelected
This is my fragment:
public class ProfileDetailFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.profile_detail_fragment, container, false);
Toolbar toolbar = (Toolbar) v.findViewById(R.id.toolbar);
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
return v;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_profile, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case android.R.id.home:
getActivity().onBackPressed();
return true;
case R.id.tick:
save();
return true;
default:
return super.onOptionsItemSelected(menuItem);
}
}
public void save(){
Toast.makeText(getContext(), "test", Toast.LENGTH_LONG).show();
}
}
This is my fragment layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="#+id/fragment_master"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
android:clickable="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</RelativeLayout>
And this is my menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="#+id/tick"
android:title="OK"
app:showAsAction="always"
tools:ignore="AppCompatResource" />
</menu>

Maybe your activity is interfering between the menu and fragment.
There are two solutions:
Inflate and handle menu within your activity and communicate with the fragment
Specify to the fragment for handle the menu: Android - How to access actionbar's menu items in fragment class
*Reviweing your code: make sure you return false inside your onOptionsItemSelected(MenuItem item) Activity's method

Related

Materialio Appbar does not hide when scrolling down

I've followed the material documentation for top app bars and implemented a part of it in my app to be able to hide it, when scrolling down my list.
My Layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/ddd"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".pkgTestforend.DriverListFragment">
<com.example.dochjavatestimplementation.pkgTestforend.CustomLinearLayout
android:layout_width="match_parent"
android:id="#+id/cusLL"
android:layout_height="match_parent"
app:elevation="0dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/listAllDrivers"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
</com.example.dochjavatestimplementation.pkgTestforend.CustomLinearLayout>
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:liftOnScroll="true" >
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/topAppBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="PageTitle"
app:menu="#menu/top_app_bar"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:navigationIcon="#drawable/baseline_menu_24"
style="#style/Widget.MaterialComponents.Toolbar.Primary"
/>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And the corresponding fragment:
public class DriverListFragment extends Fragment implements View.OnClickListener {
public DriverListFragment() {
}
public static DriverListFragment newInstance(String param1, String param2) {
return new DriverListFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_driver_list, container, false);
setUpToolbar(view);
return view;
}
private void setUpToolbar(View view) {
Toolbar toolbar = view.findViewById(R.id.topAppBar);
AppCompatActivity activity = (AppCompatActivity) getActivity();
if (activity != null) {
activity.setSupportActionBar(toolbar);
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
menuInflater.inflate(R.menu.top_app_bar, menu);
super.onCreateOptionsMenu(menu, menuInflater);
}
ListView listViewDriver;
DriverListAdapter adapter;
RoomWithRxJavaViewModel viewModel;
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
listViewDriver = view.findViewById(R.id.listAllDrivers);
viewModel = new RoomWithRxJavaViewModel(getActivity().getApplication());
Disposable d = viewModel.getDrivers()
.subscribe(allusers ->
{
adapter = new DriverListAdapter(getContext(), (ArrayList<Driver>) allusers);
listViewDriver.setAdapter(adapter);
}, e -> {
//show err mes
}
);
}
}
The setup is pretty simple build.
I just have a custom linearlayout which I am planning to modify later. And yes, the relative layout for the listview is on purpose so I can easily modify my future button positions.
The result looks like this:
So the issue is that, altough the app bar at the top is visible, it wont hide when I scroll down my list (see picture above), eventough I am using app:liftOnScroll="true" and app:layout_scrollFlags="scroll|enterAlways|snap".
What am I missing exactly? Is it cause I use my custom linearlayout?
Putting the appbar before the custom linearlayout didn't change the output unfourntatly.

Two different menus for Top App Bar and Bottom App bar with Navigation Components

I was trying out Android Navigation Architecture Component and was also looking into Material design guidelines. I really got inspired by the design below:
For the top toolbar I can set it by setSupportActionBar(toolbar) and then in MainActivity:
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return super.onCreateOptionsMenu(menu);
}
But while trying it out I cannot figure it out how to implement menus on both Top and Bottom app bars for different fragments, specially for bottom app bar.
For example, I want to show a favorite icon on bottom app bar only on DetailFragment, but on MainActivity, it should be gone.
My current codes:
MainActivity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
NavController navController = Navigation.findNavController(this, R.id.nav_host);
NavigationUI.setupActionBarWithNavController(this, navController);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show());
}
#Override
public boolean onSupportNavigateUp() {
return Navigation.findNavController(this, R.id.nav_host).navigateUp();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return super.onCreateOptionsMenu(menu);
}
}
MainFragment
public class MainFragment extends Fragment {
public MainFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_main, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button buttonOne = view.findViewById(R.id.button_one);
buttonOne.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.detailFragment));
}
}
DetailFragment
public class DetailFragment extends Fragment {
public DetailFragment() {
// Required empty public constructor
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_detail, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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:animateLayoutChanges="true"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:theme="#style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<fragment
android:id="#+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
android:layout_marginTop="?android:attr/actionBarSize"
app:defaultNavHost="true"
app:layout_anchor="#id/bottom_appbar"
app:layout_anchorGravity="top"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
app:navGraph="#navigation/mobile_navigation" />
<com.google.android.material.bottomappbar.BottomAppBar
android:id="#+id/bottom_appbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:layout_gravity="bottom" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_anchor="#id/bottom_appbar" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
mobile_navigation.xml
<navigation 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:id="#+id/mobile_navigation"
app:startDestination="#id/mainFragment">
<fragment
android:id="#+id/mainFragment"
android:name="com.example.MainFragment"
android:label="fragment_main"
tools:layout="#layout/fragment_main" >
<action
android:id="#+id/toAccountFragment"
app:destination="#id/detailFragment" />
</fragment>
<fragment
android:id="#+id/detailFragment"
android:name="com.example.DetailFragment"
android:label="fragment_account"
tools:layout="#layout/fragment_detail" />
</navigation>
menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/app_bar_settings"
android:title="#string/action_settings"
app:showAsAction="never" />
</menu>
bottom_appbar_menu.xml for DetialFragment only
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_bottom_fav"
android:icon="#drawable/ic_favorite"
android:title="#string/action_favorite"
app:showAsAction="ifRoom" />
</menu>
Any help is appreciated.
Updated with possible solution:
This is what I'm able to come up with but is not satisfied as I don't know if it's the write way to do it. I'm posting a possible solution:
1- MainActivity
NavController navController = Navigation.findNavController(this, R.id.nav_host);
NavigationUI.setupWithNavController(toolbar, navController);
2- Creating two different menus for bottom app bar (I didn't tried adding menu items dynamically), one with a blank menu xml for MainFragment and another one that contains a favorite icon for DetailFragment.
For simplicity, overriding onCreateOptionsMenu in MainActivity rather than overriding it with the MainFragment:
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
bottomAppBar.replaceMenu(R.menu.bottom_menu_blank);
return super.onCreateOptionsMenu(menu);
}
3- Thanks to #ʍѳђઽ૯ท for letting me know about replaceMenu method of Bottom App Bar. In DetailFragment use setHasOptionsMenu(true) and override onCreateOptionsMenu:
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
BottomAppBar bottomAppBar = requireActivity().findViewById(R.id.bottom_appbar);
bottomAppBar.replaceMenu(R.menu.bottom_menu_fav);
}
If anyone has a better way then please do let know.
Just use onCreateOptionsMenu() for the Toolbar as usual: (Kotlin)
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_first, menu)
return super.onCreateOptionsMenu(menu)
}
Then declare the Toolbar inside onCreate() and use setSupportActionBar():
val toolbar = findViewById<Toolbar>(R.id.myToolbar)
setSupportActionBar(toolbar)
And after that, replaceMenu() will do the trick: (Inside onCreate())
val bottomBar = findViewById<BottomAppBar>(R.id.bottomAppBar)
bottomBar.replaceMenu(R.menu.menu_main)
Note that if you wanted to use BottomSheetFragment for the NavigationView opening, you'll need setSupportActionBar in order to set menus for the BottomAppBar and I couldn't still find a way to fix this.
To have more than one Toolbar (or BottomAppBar), you will have to inflate the other one manually. When you call setSupportActionBar() and onCreateOptionsMenu(), you are essentially doing this:
private boolean inflateBottomAppBar() {
BottomAppBar bottomAppBar = findViewById(R.id.bottomAppBar);
Menu bottomMenu = bottomAppBar.getMenu();
getMenuInflater().inflate(R.menu.menu_bottom, bottomMenu);
for (int i = 0; i < bottomMenu.size(); i++) {
bottomMenu.getItem(i).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
return onOptionsItemSelected(menuItem);
}
});
}
return super.onCreateOptionsMenu(menu);
}
Where R.id.bottomAppBar is the id of the BottomAppBar and R.menu.menu_bottom is the id of the menu items.
Call this method in your onCreateOptionsMenu() after you inflate the main toolbar and you will be good to go. All the item clicks will be handled normally by the onOptionsItemSelected() method.
This will also work if you are making two or more regular toolbars.

Toolbar arrow don't work in PreferenceFragment in NavigationDrawer

I'm using PreferenceFragment that I launch from the NavigationDrawer. In the preferenceFragment I show the toolbar. All looks good, but when I press the arrow in the toolbar to come back it doesn't work. I can see in the log this:
D/ViewRootImpl: ViewPostImeInputStage ACTION_DOWN
But don't do nothing. The only way to come back is press button back in the device
Some help will be appreciated.
This is my code:
PreferenceFragment:
public class AnPreferenceActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pref_with_actionbar);
getFragmentManager().beginTransaction()
.replace(R.id.content_frame, new SettingsPreference())
.commit();
}
public static class SettingsPreference extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
}
}
}
Layout xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
app:navigationContentDescription="#string/abc_action_bar_up_description"
app:navigationIcon="?attr/homeAsUpIndicator"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:title="#string/action_settings" />
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/appbar" />
</RelativeLayout>
In onCreate() of your fragment first you should declare it has option Menu:
setHasOptionsMenu(true);
Then you should handle your own menu:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// close fragment here
getActivity().getFragmentManager().popBackStack();
return true;
default:
break;
}
return false;
}
If you need more Menu create new menu item:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.fragment_menu, menu);
return true;
}
Accessing to toolbar from fragment is not good approach you should handle it in better way. There are lots of thread about this issue out there. (in fact Activity should handle Toolbar behaviour)

How to go back by using action bar in android

I have to get this as a result:
Moreover, how do I add colour in the action bar's background?
Try this
Create toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#color/orange"
android:theme="#style/AppTheme.Toolbar.Theme"/>
Include this toolbar in your activitys xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/app_bg"
android:orientation="vertical"/>
<include
android:id="#+id/tool_bar"
layout="#layout/toolbar" />
</LinearLayout>
create your activity.java
public class MainActivity extends AppCompatActivity {
protected ActionBar actionBar;
protected Toolbar toolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_profile);
toolbar = (Toolbar) findViewById(R.id.tool_bar);
setSupportActionBar(toolbar);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish(); //here add your back function code.
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
Click here to add back action button in your actionbar and add onClick methocd for that button.
For adding background to your actionbar refer this.
Hope this helps.

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 -
Suppose there are 5 tabs. 2nd and 3rd tab has viewpager within each consist of two more fragments within
1st tab has no menu
2nd tab has menu_2 (only for 2nd child fragment)
3rd tab again has no menu
4th tab has menu_4 (only for 1st child fragment)
5th tab has menu_5
Initially, there should be no menu for tab 1 which is ok. Then moving to 3rd tab directly it shows menu_4 which by default should show no menu. Then sliding to tab 4, it will show proper menu_4 then sliding back to 3rd tab it will show no menu (which is as required).
This same thing happens for tab 5. If I am switching to 2nd child fragment within 2nd tab then the same behavior is observed with 1st tab.
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?
I have written small test-application to check the behaviour.
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

Categories

Resources