So I have this toolbar in my app, and I want to display different menu items depending on whether the user is logged in or not. When the user logs in or out I want to update my layout which for now is the toolbar menu to represent the change. For some reason though, my menu items are not removed at all, all of them are visible at all times, regardless of the login state.
My menu items:
<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/action_add"
android:icon="#drawable/ic_action_add"
android:title="#string/action_add"
app:showAsAction="ifRoom"/>
<item
android:id="#+id/action_login"
android:icon="#drawable/ic_action_logged_out"
android:title="#string/action_log_in"
app:showAsAction="always"/>
<item
android:id="#+id/action_logout"
android:icon="#drawable/ic_action_logged_in"
android:title="#string/action_log_out"
app:showAsAction="always"/>
</menu>
The first menu item is only supposed to be visible to a user that is logged in. Also I guess it's self explanatory but I only want one of the log in/out buttons to be visible depending on the login state.
My Java code:
protected void initializeToolbar(){
myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
menu = myToolbar.getMenu();
resetToolbarMenu();
myToolbar.setOnMenuItemClickListener(this);
}
private void resetToolbarMenu(){
menu.clear();
getMenuInflater().inflate(R.menu.menu, menu);
if(loginPrefs.getBoolean("login", false)) { //loginPrefs is reference to a SharedPreference object
menu.removeItem(1);
} else {
menu.removeItem(2);
menu.removeItem(0);
}
}
Reason I got two methods is that I only want to set up the toolbar and listener once, but I want to be able to change the menu every time the user logs in/out without reloading the page.
After a successful login the resetToolbarMenu() method will be called again.
I suppose the menu.remove(0) does not update the UI, but I could not find another way to reach my menu object without first inflating it and then getting it from the toolbar, and I assume the inflation is what decides what items are visible. Basically, I could not find a way to remove any menu items before inflating or updating the UI in another way than inflating.
Solution:
I changed my java code into something like this:
protected void initializeToolbar(){
myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
setSupportActionBar(myToolbar);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return super.onCreateOptionsMenu(menu); // Don't know if this is necessary or if returning true is prefered.
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
if(loginPrefs.getBoolean("login", false)) {
menu.getItem(0).setVisible(true);
menu.getItem(1).setVisible(false);
menu.getItem(2).setVisible(true);
} else {
menu.getItem(0).setVisible(false);
menu.getItem(1).setVisible(true);
menu.getItem(2).setVisible(false);
}
return true;
}
I also had to call invalidateOptionsMenu() on logout/login to update the toolbar/menu. However onPrepareOptionsMenu() is also automatically called whenever you open the menu for items that aren't shown on the toolbar itself.
PS: OnCreateOptionsMenu and OnPrepareOptionsMenu will not be used unless you remember to setSupportActionBar(myToolbar) as I forgot.
You can create options menu by overriding onCreateOptionsMenu. You can create 2 xml and inflate either one of them depending on your logic. To force a redraw of the menu, you can call invalidateOptionsMenu.
For example
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
if (login) {
inflater.inflate(R.menu.login_menu, menu);
} else {
inflater.inflate(R.menu.logout_menu, menu);
}
return true;
}
And outside change the flag and force redraw
login = false; // or true
invalidateOptionsMenu();
You have to override onPrepareOptionsMenu in the activity to change the items in the menu.
From the 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.
Related
I've tried already all possible solutions. Here's my code:
private Menu mMenu;
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_article_detail_menu, menu);
mMenu = menu;
}
void changeStar(boolean added) {
if (mMenu != null) {
MenuItem item = mMenu.findItem(R.id.favourites_item);
if (added) {
Log.d(LOG_TAG, "Set full icon");
item.setIcon(getResources().getDrawable(R.drawable.star_full));
} else {
Log.d(LOG_TAG, "Set empty icon");
item.setIcon(getResources().getDrawable(R.drawable.star_empty));
}
}
}
Here is my menu xml file:
<menu 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"
tools:context="ssidit.pp.ua.payspacereader.ArticleDetailActivity">
<item
android:id="#+id/refresh_item"
android:title="#string/refresh"
app:showAsAction="never"></item>
<item
android:id="#+id/favourites_item"
android:icon="#drawable/star_empty"
android:title="#string/add_to_favourite"
app:showAsAction="ifRoom"></item>
<item
android:id="#+id/share_item"
android:icon="#drawable/ic_share"
android:title="#string/share"
app:actionProviderClass="android.support.v7.widget.ShareActionProvider"
app:showAsAction="ifRoom"></item>
</menu>
invalidateMenu() method doesn't help. When I call setIcon method, nothing changes on my android device.
Here is my code:
private boolean isFavourite;
private void setValues(Cursor cursor) {
Log.d(LOG_TAG, "Setting values");
setData(titleTextView, CursorUtility.getTitle(cursor));
setData(dateTextView, CursorUtility.getDateText(cursor));
setData(timeTextView, CursorUtility.getTimeText(cursor));
isFavourite = CursorUtility.isFavourite(cursor);
getActivity().invalidateOptionsMenu();
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
Log.d(LOG_TAG, "OnPrepareOptionsMenu");
MenuItem item = menu.findItem(R.id.favourites_item);
if (isFavourite) {
Log.d(LOG_TAG, "Set full icon");
item.setIcon(R.drawable.star_full);
} else {
Log.d(LOG_TAG, "Set empty icon");
item.setIcon(R.drawable.star_empty);
}
}
As you can see, everything is logged. So there can't be mistake if some method doesn't call. Also I checked item by getting title of it. It is right item. Just some kind of black magic.
Try using invalidateOptionsMenu and move your changeStar logic to onPrepareOptionsMenu. From Android documentation:
public boolean onPrepareOptionsMenu (Menu menu)
Added in API level 1
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.
The default implementation updates the system menu items based on the activity's state. Deriving classes should always call through to the base class implementation.
Firstly: make a global variable of menu
Secondly: wherever in activity you want to change the icon just get that menu item by global variable menu using getItem() method instead of findItem.
Thirdly: set the icon to your menuItem returned by getItem() as follow menuItem.setIcon(res)
My menu has a item to Log in, but when you are logged in I want it to say Log out.
How?
If I'm going to change the item after its created, its probably through this method
#Override
public boolean onCreateOptionsMenu( Menu menu ) {
MenuInflater inflater = getMenuInflater();
inflater.inflate( R.menu.menu_main, menu );
return true;
}
Afaik the onCreateOptionsMenu() happens after the onCreate so putting any getItemId() for the menu there will give me a NullPointerException right away.
I want the app to find out if its supposed to use the string R.string.Logout if its logged in.
I dont even know what to search for for this issue. All I found was how to make a string implement names, like this answer https://stackoverflow.com/a/7646689/3064486
You should use onPrepareOptionsMenu(Menu menu) instead to update menu items
#Override
public boolean onPrepareOptionsMenu(Menu menu){
super.onPrepareOptionsMenu(menu);
MenuItem someMenuItem = menu.findItem(R.id.some_menu_item);
someMenuItem.setTitle("Log out");
return super.onPrepareOptionsMenu(menu);
}
To refresh Menu items call invalidateOptionsMenu();from Activity
From Android API guides: "If you want to modify the options menu based on events that occur during the activity lifecycle, you can do so in the onPrepareOptionsMenu() method. This method passes you the Menu object as it currently exists so you can modify it, such as add, remove, or disable items. (Fragments also provide an onPrepareOptionsMenu() callback.)"
After you inflate a menu, you can customize its items. To get each one, you must call findItem() with the item's id. In particular, you can use setTitle() to change the displayed string.
For example:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
if (mIsLoggedIn)
menu.findItem(R.id.action_login).setTitle("Log out");
return true;
}
where action_login is the id you set for this particular menu item in the menu's xml file.
private void updateUI() {
runOnUiThread(new Runnable() {
#Override
public void run() {
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
Menu menu = navigationView.getMenu();
MenuItem nav_signin = menu.findItem(R.id.nav_signin);
nav_signin.setTitle(MyApp.signedIn ? "Sign out" : "Sign in");
}
});
}
I am trying to show/hide items in my action bar depending on which fragment is visible.
In my MainActivity I have the following
/* Called whenever invalidateOptionsMenu() is called */
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
if(this.myFragment.isVisible()){
menu.findItem(R.id.action_read).setVisible(true);
}else{
menu.findItem(R.id.action_read).setVisible(false);
}
return super.onPrepareOptionsMenu(menu);
}
This works great however, when the device is rotated there is a issue. After the rotation is complete onPrepareOptionsMenu is called again however this time this.myFragment.isVisible() returns false...and hence the menu item is hidden when clearly the fragment is visible (as far as whats shown on the screen).
Based on the Fragments API Guide, we can add items to the action bar on a per-Fragment basis with the following steps:
Create a res/menu/fooFragmentMenu.xml that contains menu items as you normally would for the standard menu.
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/newAction"
android:orderInCategory="1"
android:showAsAction="always"
android:title="#string/newActionTitle"
android:icon="#drawable/newActionIcon"/>
</menu>
Toward the top of FooFragment's onCreate method, indicate that it has its own menu items to add.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
...
}
Override onCreateOptionsMenu, where you'll inflate the fragment's menu and attach it to your standard menu.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fooFragmentMenu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
Override onOptionItemSelected in your fragment, which only gets called when this same method of the host Activity/FragmentActivity sees that it doesn't have a case for the selection.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.newAction:
...
break;
}
return super.onOptionsItemSelected(item);
}
Try using Fragment's setRetainInstance(true); when your fragment is attached to the Activity. That way your fragment will retain it's current values and call the lifecycle when device rotated.
Edit: This is a quick and dirty fix, see es0329's answer below for a better solution.
Try adding this attribute to your activity tag in your android manifest:
android:configChanges="orientation|screenSize"
In my action bar I have 2 menu items:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/refresh"
android:title="Refresh"
android:icon="#drawable/refresh"
android:showAsAction="ifRoom" >
</item>
<item android:id="#+id/back"
android:title="Back"
android:icon="#drawable/back"
android:showAsAction="ifRoom" >
</item>
</menu>
I'm trying to turn "invisible" my menu item "Refresh", when I call the function refreshinvisible(), the Refresh item goes away, but now the action bar shows two "back" items... Why? (I'm using SherlockActionBar)
My refreshinvisible() function:
public void refreshinvisible(){
MenuItem item = menu.findItem(R.id.refresh);
item.setVisible(false);
}
Anyone know how to proceed?
There is a method called onPrepareOptionsMenu() which is called every time right before the menu is shown, i.e. before onCreateOptionsMenu() is called. You can use the activity's invalidateOptionsMenu() method to trigger a redraw of the options menu.
Hence, you can easily re-create your menu taking into account certain conditions.
Here's some code. Define two booleans as fields of your class, for example:
private boolean showRefresh;
private boolean showBack;
Override the onPrepareOptionsMenu() method and set the menu item's visbility depending on the respective boolean:
#Override
public boolean onPrepareOptionsMenu( Menu menu ) {
super.onPrepareOptionsMenu( menu );
menu.findItem( R.id.refresh ).setVisible( showRefresh );
menu.findItem( R.id.back ).setVisible( showBack );
return true;
}
No every time you want to change the visibility of a certain menu item, set the respective boolean accordingly and call the invalidateOptionsMenu() method.
You can read this.
Changing menu items at runtime
Once the activity is created, the
onCreateOptionsMenu() method is called
only once, as described above. The
system keeps and re-uses the Menu you
define in this method until your
activity is destroyed. If you want to
change the Options Menu any time after
it's first created, you must override
the onPrepareOptionsMenu() method.
This passes you the Menu object as it
currently exists. This is useful if
you'd like to remove, add, disable, or
enable menu items depending on the
current state of your application.
E.g.
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
MenuItem item = menu.findItem(R.id.refresh);
item.setVisible(false);
return true;
}
In some methods of my Activity I want to check the title of menu or know if it is checked or not. How can I get Activity's menu. I need something like this.getMenu()
Be wary of invalidateOptionsMenu(). It recreates the entire menu. This has a lot of overhead and will reset embedded components like the SearchView. It took me quite a while to track down why my SearchView would "randomly" close.
I ended up capturing the menu as posted by Dark and then call onPrepareOptionsMenu(Menu) as necessary. This met my requirement without an nasty side effects. Gotcha: Make sure to do a null check in case you call onPrepareOptionsMenu() before the menu is created. I did this as below:
private Menu mOptionsMenu;
#Override
public boolean onCreateOptionsMenu(final Menu menu) {
mOptionsMenu = menu
...
}
private void updateOptionsMenu() {
if (mOptionsMenu != null) {
onPrepareOptionsMenu(mOptionsMenu);
}
}
Call invalidateOptionsMenu() instead of passing menu object around.
you could do it by passing the Menu object to your Activity class
public class MainActivity extends Activity
{
...
...
private Menu _menu = null;
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
_menu = menu;
return true;
}
private Menu getMenu()
{
//use it like this
return _menu;
}
}
There several callback methods that provide menu as a parameter.
You might wanna manipulate it there.
For example:
onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)
onCreateOptionsMenu(Menu menu)
onCreatePanelMenu(int featureId, Menu menu)
There several more, best you take a look in activity documentation and look for your desired method:
http://developer.android.com/reference/android/app/Activity.html
As far as I could understand what you want here is which may help you:
1. Refer this tutorial over option menu.
2. Every time user presses menu button you can check it's title thru getTitle().
3. Or if you want to know the last menu item checked or selected when user has not pressed the menu button then you need to store the preferences when user presses.
Android now has the Toolbar widget which was a Menu you can set/get. Set the Toolbar in your Activity with some variation of setSupportActionBar(Toolbar) for stuff like onCreateOptionsMenu from a Fragment for example. Thread revived!