I'm storing one of the menu items from my activity as a class field, when inside onCreateOptionsMenu().
_myMenuItem = (MenuItem) findViewById(R.id.menu_item);
But in onCreate() I call an async method, and the result of that dictates whether I should make the visibility true or false on said menu item; for example:
if (someCondition) {
_myMenuItem.setVisible(true);
}
The problem is sometimes the async method completes (and that above code is called) before the menu is inflated, causing a null pointer exception.
How can I resolve this timing issue? Ideally without putting the async call inside onCreateOptionsMenu() if anyone knows of a good way. Thanks
Declare a boolean property in the Activity. After completing the background work set that property to true if menu item is to be shown, to false otherwise and force the menu to be refreashed (call invalidateOptionsMenu). Refreshing options menu causes onPrepareOptionsMenu to be called. In onPrepareOptionsMenu check the value of your property and set the visibility of menu item.
Related
Why when I click on some button in AppBar, first is called onOptionsItemSelected from Activity and then (if I return false) the onOptionsItemSelected on Fragment is called?
I thought that if there is override for onOptionsItemSelected on Fragment, it gets called first and calls Activity onOptionsItemSelected only if I return false.
It is possible that someone changed the order in some configuration? I am working on quite large project and I am not sure if someone not altered default behavior.
I am currently learning Robolectric to test for Android and I am having trouble obtaining my application's menu.
Right now, Robolectric's getOptionsMenu() is returning null. The code itself works fine but the test always returns null for the options menu.
My code is the following
#Test
public void onCreateShouldInflateTheMenu() {
Intent intent = new Intent();
intent.setData(Uri.EMPTY);
DetailActivity dActivity = Robolectric.buildActivity(DetailActivity.class, intent).create().get();
Menu menu = Shadows.shadowOf(dActivity).getOptionsMenu(); // menu is null
MenuItem item = menu.findItem(R.id.action_settings); // I get a nullPointer exception here
assertEquals(menu.findItem(R.id.action_settings).getTitle().toString(), "Settings");
}
Does anyone know why Robolectric is returning null? Did I miss any dependencies?
The onCreateOptionsMenu will be called after oncreate so to make sure that you can see your menu try
Robolectric.buildActivity(DetailActivity.class, intent).create().resume().get();
or you can make sure the activity is visible
Robolectric.buildActivity(DetailActivity.class, intent).create().visible().get();
From docs
What’s This visible() Nonsense?
Turns out that in a real Android app, the view hierarchy of an
Activity is not attached to the Window until sometime after onCreate()
is called. Until this happens, the Activity’s views do not report as
visible. This means you can’t click on them (amongst other unexpected
behavior). The Activity’s hierarchy is attached to the Window on a
device or emulator after onPostResume() on the Activity. Rather than
make assumptions about when the visibility should be updated,
Robolectric puts the power in the developer’s hands when writing
tests.
So when do you call it? Whenever you’re interacting with the views
inside the Activity. Methods like Robolectric.clickOn() require that
the view is visible and properly attached in order to function. You
should call visible() after create().
Android: When is onCreateOptionsMenu called during Activity lifecycle?
I have a Toolbar being used as an ActionBar with two items. I only want to ever display one at a time as they kind of replace each other. The problem is that when i replace a Fragment, it call onCreateOptionsMenu and will inflate the menu again, meaning that the same action button will be shown, even if the other one was previously in the ActionBar. I have to need to change anything in the ActionBar from my Fragments or when a new Fragment is displayed(with FragmentManager.FragmentTransaction.replace()). So my question is how do I not call onCreateOptionsMenu when a new fragment is displayed?
I can't use a boolean because I will still need it to reinflate on orientation change. And any advice on how to handle orentation change for my situation?
I can post code, but it seems more conceptual and I'm not sure that it would help.
I solved the problem by instead of not calling onCreateOptionsMenu, I added the items to my menu manually.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean refreshVisible;
if (refreshItem != null && refreshItem.isVisible()){//is being displayed prior to inflation
refreshVisible = true;
}else if (refreshItem == null){//it's null so the menu has never been created
refreshVisible = true;
}else {//it's not null and invisibe, other icon was being displayed
refreshVisible = false;
}
menu.clear();//clear menu so there are no duplicate or overlapping icons
getMenuInflater().inflate(R.menu.main, menu);//inflate menu
refreshItem = menu.findItem(R.id.refresh);
useDataItem = menu.findItem(R.id.use_data);
refreshItem.setVisible(refreshVisible);//if menu is being created for first time or item was previously visible, then display this item
useDataItem.setVisible(!refreshVisible);//display this item if not displaying other
return true;
}
I would fiddle with the onPrepareOptionsMenu hook. If you can detect that your menu should not be shown you should jest return false from there. Per documentation:
Prepare the Screen's standard options menu to be displayed. This is called right before the menu is shown, every time it is shown. You can use this method to efficiently enable/disable items or otherwise dynamically modify the contents.
and
You must return true for the menu to be displayed; if you return false it will not be shown.
You can call setHasOptionsMenu(false); inside your fragment.
This will prevent onCreateOptionsMenu() from being called when that fragment added.
Im looking to set an actionbar menu item visible from a helper class. Is it possible to access the actionbar Menu outside of onOptionsItemSelected through a reference to an activity? code below.
public boolean getMenuFromActivity(BaseActivity activity){
// something like Menu menu = activity.getActionBar().getMenu()?
// then get menu item by id and set visibility..
//return true if found
return false
}
Simple answer: Call findItem() on the Menu after you have inflated your menu resource in onCreateOptionsMenu(), and hold onto that MenuItem in a data member of your activity, so you can use it later.
Slightly less-simple answer: Hold onto the Menu from onCreateOptionsMenu() in a data member of your activity, and use it later to find your item.
I've got an optionsmenu looking like this right now:
Lets say that if I click item 1, i want two new items added to the menu looking like this:
I'm having problems doing this at runtime(while it's open) since onCreateOptionsMenu is only called once and onPrepareOptionsMenu seems only to be called when the menubutton of the phone is clicked. I just want it to refresh with these new items added.
Thanks for any help!
When you select an option item it causes the system to close the options menu. This happens after onOptionsItemSelected() runs. I'm guessing what you need to have happen is for that entire process to complete then have the options menu programmatically opened again so onPrepareOptionsMenu() is called.
Try this:
In onOptionsItemSelected(), when the user selects your "add two more options" option, set a flag in your activity but don't do anything else.
Override onOptionsMenuClosed(). Check if the flag is set. If it is, then post to be executed by the UI thread on its next pass. It should do nothing but open the options menu again, e.g.
runOnUiThread(new Runnable() {
#Override
public void run() {
openOptionsMenu();
}
});
Override onPrepareOptionsMenu(). Check if the flag is set. If it is, then add your two additional menu items and un-set the flag. You'll need to do some additional work to prevent the "add more items" menu item from continuing to add new items every time its pressed, unless that's the behavior you're looking for.