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.
Related
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.
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.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.my_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
This question was already asked here. And i tried all the answers its not working.
My issue is
Everything working fine.but when i open another fragment(it have not any option menu) and get back to previous view pager fragment while clicking menu item onOptionsItemSelected not firing. When i swipe fragment in viewpager and come back to the previous one, when i click menu item its firing.
Its because viewpager maintain 3 fragment alive at a time. so when you come back, it set menu visibility status true to last fragment. thats why your menu item click not firing.
Use the following in the fragment where you keeping a viewpager in your case fragment A.
private boolean isInitial=true;
#Override
public void onResume() {
super.onResume();
if (!isInitial) {
int pos = viewpager.getCurrentItem();
if (pageAdapter.getItem(pos).getUserVisibleHint() && pageAdapter.getItem(pos).isVisible()) {
pageAdapter.getItem(pos).setMenuVisibility(true);
}
} else {
isInitial = false;
}
}
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());
}
My application is basically an online store that has a cart. The button to start the cart is in the ActionBar. When someone presses on a product it starts an animation where the product quicly "slides" through the screen towards the ActionBar cart button. As soon as that finishes the cart "blinks". To blink the cart I use
ValueAnimator cartAnim = ObjectAnimator.ofFloat(mCartItem, "alpha", 1,
0.25f, 0);
Where mCartItem is the ActionBar Item View to be animated.
Now as it turns out getting the View of the actual ActionBar item is kinda hard. I can get the View in onOptionsItemSelected but that's basically it, however this won't work for me since the animation isn't triggered from the ActionBar, it's triggered from a ListView in the main UI. After some googling I did however find a hack around this, that works:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.cartmenu, menu);
new Handler().post(new Runnable() {
#Override
public void run() {
mCartItem = findViewById(R.id.theitem);
}
});
return(super.onCreateOptionsMenu(menu));
}
Why is this way working? As opposed to:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.cartmenu, menu);
mCartItem = findViewById(R.id.theitem); // Always ends up null.
return(super.onCreateOptionsMenu(menu));
}
How would you solve the problem I had?
findViewById()
searches for a child view with the given id in "this" view. When you call it in onCreateOptionsMenu() the menu is still not attached to the main view, so you it cannot find your item.
Using Handler().post(new Runnable()...) the findViewById(R.id.theitem) is executed after all your view is created and, of course, even the menu has been attached to your main view, so it can be found.
Better solution:
since you are inflating your cart menu in the menu object, you can use:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.cartmenu, menu);
mCartItem = menu.findItem(R.id.theitem); //This will find your menu item! :-)
return(super.onCreateOptionsMenu(menu));
}
I am having trouble getting the following piece of code to work out. I have a viewpager with 3 fragments, and I want a search icon to only show up on one. I started off trying to add the search function by the fragment, but the rendering of the menu item was slow when swiping to that page. I am now on the part to add the search icon to the activity, and then just hide or show depending on which viewpager page is active, but the following is not working:
public class MyApp extends FragmentActivity implements
FragmentTeams.FragmentNotification,ViewPager.OnPageChangeListener,
OnNavigationListener{
...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
menuSearch = menu.findItem(R.id.menu_search);
mSearchView = new SearchView(this);
menuSearch.setActionView(mSearchView);
menuSearch.setVisible(false);
return true;
}
#Override
public void onPageSelected(int pageNum) {
if(pageNum== 1){
ActionBar actionBar = MyApp.this.getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
menuSearch.setVisible(true);
invalidateOptionsMenu();
}else{
ActionBar actionBar = MyApp.this.getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
menuSearch.setVisible(false);
invalidateOptionsMenu();
}
}
While the above does (appear to) create and hide the icon at onCreateOptionsMenu, it is not reenabled when moving to
pageNum ==1
Can anyone give me some insight as to why this may be happening?
invalidateOptionsMenu make the system calls the method onPrepareOptionsMenu, so you can override this method as follows:
public boolean onPrepareOptionsMenu(Menu menu) {
int pageNum = getCurrentPage();
if (pageNum == 1) {
menu.findItem(R.id.menu_search).setVisible(true);
}
else {
menu.findItem(R.id.menu_search).setVisible(false);
}
}
public void onPageSelected(int pageNum) {
invalidateOptionsMenu();
}
You can implement onCreateOptionsMenu() in your Fragment and set 'setHasOptionsMenu(true)' for the fragment
a possible solution for this problem would be inflating your custom menu inside the activity hosts your ViewPager and getting a menu reference as below:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.custom_menu, menu);
customMenu = menu;
return super.onCreateOptionsMenu(menu);
}
after that you can easily hide/show the menu's items without any delay inside onPageSelected method as below:
#Override
public void onPageSelected(int position) {
switch (position) {
case 0: {
customMenu.getItem(0).setVisible(false);
break;
}
case 1: {
customMenu.getItem(0).setVisible(true);
break;
}
}
I used Nermeen's answer and managed to get it without any delay.
I don't inflate anything in onCreateOptionsMenu, but use it to get a reference to the menu:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
customMenu = menu;
return super.onCreateOptionsMenu(menu);
}
Then, in onPrepareOptionsMenu(), I call the viewpager getCurrentItem() (should be something like viewPager.getCurrentItem()), call invalidateOptionsMenu() and inflate whatever menu I want in that page using the customMenu reference I created in onCreateOptionsMenu().