Facing issue while inflating different Menu in different fragment.
My application hierarchy is like:
BottomNavigationView --> Fragments(4) ---> Tab+ViewPager --> Fragments(3)
Each fragment contains ViewPager and ViewPager contains multiple fragments.
Please see the attached image for more clarity.
Issue: It's keep on adding new menu, or sometimes carry previous screen menu.
I have tried using "menu.clear()", "getActivity().invalidateOptionsMenu();"
You have ViewPager, it means that if you go to ith position, fragments that are on position i-1 and i+1 will also be created. So their onCreateOptionsMenu will be called and even if you call menu.clear() it will not work properly.
Solution is to change menu in container Fragment/Activity of ViewPager , in following way. Add ViewPager.OnPageChangeListener listener to ViewPager and in onPageSelected(int position) method change menu yourself:
#Override
public void onCreateOptionsMenu(final Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
int menuResourceId = getMenuResourceByPosition(position);
menu.clear();
inflater.inflate(menuResourceId, menu);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
create method getMenuResourceByPosition that will give you proper menu resource id by ViewPager's selected position.
Related
When swiping between tabs on my application, the menu icons have a distinct delay before they appear. If I click tabs, rather than swiping, they update immediately. I have different menu.xml files for each fragment, and inflate them inside each fragment's onCreateOptionsMenu.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fodmap_menu, menu);
final MenuItem item = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
searchView.setOnQueryTextListener(this);
}
Notice the icon changes from the overflow to the magnifying glass instantly when the tabs are clicked, but distinctly delayed when swiping. I would like the icon to be updated as soon as the new tab is centered. On Pocket Cast's Discover menu the tabs with different menu icons seem to load them even before the swipe animation completes.
Instead of using a different menu inside each fragment of the view pager - inflate the menu, call invalidateOptionsMenu() inside the ViewPager's onPageChangeListener, and programmatically display desired menu icon's in onCreateOptionsMenu, all inside the main activity instead of the fragments. The searchView listener is still handled in the fragment.
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
invalidateOptionsMenu();
}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {}
});
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.fodmap_menu, menu);
if (mViewPager.getCurrentItem()==0){
menu.findItem(R.id.action_search).setVisible(false);
} else if(mViewPager.getCurrentItem()==1){
menu.findItem(R.id.action_search).setVisible(true);
} else if(mViewPager.getCurrentItem()==2) {
menu.findItem(R.id.action_search).setVisible(false);
}
return super.onCreateOptionsMenu(menu);
}
There is no delay now and the menu icons update before the swipe animation finishes.
Never had this issue with ViewPager. But the new ViewPager2 acts this way. And it is frustrating because there is no way to change the scroll speed since the default is toooo slow.
I all want is when I slide to other page on viewpager, specified navigaton drawer item should be checked. I know that I have to do it in viewpager listener, but how? I couldnt manage it. Thanks in advance.
You need to add addOnPageChangeListener to your ViewPager and in your onPageSelected(int position) you have to enable the checked state of your navigation menu item.
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
navigationView.getMenu().getItem(0).setChecked(true); // where 0,1,2.. etc. are the indexes/positions for your menu items
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
So based on the viewpager tab selected you want to make the navigation drawer item selected so you can do the following:
1.Get onTabSelected() listener
2.inside that method check which tab is selected
3.Based on the item selected you can use this method navigationView.getMenu().getItem(0).setChecked(true);
Comment below if you need any further info
I use a NavigationDrawer pattern that is implemented in my hostactivity MenuActivity. My navigation has 3 items: Item 1, Item 2, Item 3.
Each itemis bonded to a fragment.
When I click on Item 1, I displayed a fragment A that implements a ViewPager with several fragments (nested fragments).
In my nested fragments, I inflate a menu with the following method (It works fine !) :
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.my_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
But when I click on another element of my menu (Item 2 -> display Fragment B or Item 3->display Fragment C), my menu (which was inflated in my nested fragment) is always visible but I want it to disappear.
Would you have a solution to this problem? Thank you in advance.
Just save child fragment and then override setMenuVisability:
#Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if (savedFragment!= null)
savedFragment.setMenuVisibility(menuVisible);
}
it works for me
When I encountered this problem, I solved it by setting setHasOptionMenu(true) for both the child fragment and the "root" fragment. If the "root" fragment or another child fragment doesn't use option items, it's fine anyway, since you only inflate a menu in the child fragment needing it.
I just came across the problem and solved it by the following:
#Override
public void onDestroyOptionsMenu() {
this.setMenuVisibility(false);
super.onDestroyOptionsMenu();
Log.e(TAG, "onDestroyOptionsMenu");
}
#Override
public void onDestroyView() {
onDestroyOptionsMenu();
super.onDestroyView();
}
I noticed that onDestroyOptionsMenu is not being called so what I only did is call it from the OnDestroyView method and I set the menu visibility to false.
A slightly different approach from SafiS answer:
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(...) {
View view = ...
setMenuVisibility(true);
return view;
}
#Override
public void onDestroyView() {
setMenuVisibility(false);
super.onDestroyView();
}
Add setRetainInstance(true) and setHasOptionMenu(true) in onCreate() in fragment.
I've got three fragments in a viewpager.
Two of these fragments have their own version of the onCreateOptionsMenu method:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// Set up 1 action button
inflater.inflate(R.menu.home_snapshot_add, menu);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// Set up 2 action buttons
inflater.inflate(R.menu.home_snapshot_send, menu);
}
The home activity has a basic onCreateOptionsMenu method:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
return false;
}
In the onCreate method, each fragment calls the method:
setHasOptionsMenu(true);
Each of the menu items have the tag:
android:showAsAction="always"
Seems like as I open the Activity, all three buttons appear.
However, when I scroll through them, the wrong ones magically disappear.
It feels like the activity is calling every Fragment's options menu on Activity creation and then changes the menu appropriately when I swipe left and right.
I've checked the menus but not sure what's wrong.
Anything you reckon I need to check? I'm a little out of ideas.
Thanks!
In your ViewPager's OnPageChangeListener and after setting the adapter to the ViewPager, have this:
#Override
public void onPageSelected(int position){
invalidateFragmentMenus(position);
}
private void invalidateFragmentMenus(int position){
for(int i = 0; i < mViewPagerFragentAdapter.getCount(); i++){
mViewPagerAdapter.getItem(i).setHasOptionsMenu(i == position);
}
invalidateOptionsMenu(); //or respectively its support method.
}
After setting your fragment adapter call the same method with following argument:
invalidateFragmentMenus(mViewPager.getCurrentItem());
The above statements will prevent all other fragments not to receive call on onCreateOptionsMenu() method when invalidateOptionsMenu() is called, only the currently visible fragment will receive and be able to populate the options menu.
I've used this and it has worked for me:
//In your Fragment
#Override
public void onResume() {
super.onResume();
setHasOptionsMenu(isVisible());
}
i wrote a FragmentActivity with a ViewPager, i did not want to use the actionbar for the tabs (because i did not want the tabs to be positioned over the menu) and also not the PagerTabStrip solution, because i could not customize it as i wanted.
so i wrote an own linearlayout as tabs container:
<LinearLayout
android:id="#+id/main_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:orientation="horizontal" >
</LinearLayout>
with an View.OnClickListener for each of each tab
OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(final View view) {
mapTabs.get(lastSelectedTab).setSelected(false);
view.setSelected(true);
Runnable runnable = new Runnable() {
#Override
public void run() {
int index = Integer.parseInt(view.getTag().toString());
FragmentActivity.setTab(index);
lastSelectedTab = index;
}
};
handler.post(runnable);
}
};
}
public static void setTab(int index) {
FragmentActivity.viewPager.setCurrentItem(index, true); // true or false does not make any difference
}
this is the method i wrote to add the single tabs to the tab container (main_tabs):
private void addTab(LayoutInflater inflater, ViewGroup tabs, int imageId, View.OnClickListener onClickListener) {
inflater.inflate(R.layout.main_tab, tabs);
int index = tabs.getChildCount() - 1;
ImageView ivTab = (ImageView) tabs.getChildAt(index);
ivTab.setTag(String.valueOf(index)); // stored the tab index
ivTab.setImageResource(imageId);
ivTab.setOnClickListener(onClickListener);
mapTabs.put(index, ivTab); // add an imageview to the tab
}
i used the runnalbe so that i see the highlighting of the tab immediately, and not after the slight delay when the new page appears. i also stored the last selected tab in order to deselect it when the new one gets selected.
i store all tabs in a map in order to access them later for selection.
this is the OnPageChangeListener i wrote (to highlight the associated tab):
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
mapTabs.get(position).setSelected(true); // selected the tag asociated with the current visible page
mapTabs.get(lastSelectedTab).setSelected(false); // deselectes the last selected tab
lastSelectedTab = position;
ListFragment<?> item = FragmentActivity.fragmentAdapter.getItem(position);
setTitle(item.getTitle());
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
everything works fine, i am just not clear about the performance:
when i scroll to the next page, everything goes very fast and smooth. the next page appears immediately without any dealy; only the menu in the action bar (created in the onCreateOptionsMenu(Menu menu, MenuInflater inflater) method of each Fragment) appears with a slight delay (about 500-700 ms).
but when i click on one of the tabs, the selected page appears not immediately but with a slight delay (actually the same time the menu needs to appear as mentioned above: menu and page appear together at the same time). bzw. this happens also when i use the tabstrip solution.
i guess if its possible to achieve a delay-less page change by scrolling, it should be possible somehow to achieve the same performance for the tab clicks.
but what do i do wrong?
and is there a possibility to make the menu appear immediately just like the pages when scrolling? can the different menus not be cached just like the pages?
the solution was extremely simple. i just had to keep all my 4 tabs in the ViewPager so that they don't need to get recreated (and therefore don't cause the short delay after each tab-clicki). i just had to add following lines to the onCreate method.
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setOffscreenPageLimit(4);
to avoid delay between tabs click in view pager should set the limit of tabs like
pager.setOffscreenPageLimit(4);
try this
myViewPager.animate().translationX(0f).setDuration(1000);
where 1000 is delay in milliseconds