How to change the ActionBar menu item's icon dynamically
Asked Answered
F

5

14

I want to change the menu item's icon dynamically as I get notification from a server. However, I'm getting a NullPointerException when the codes to change the menu item's icon run.

Codes I used to change the menu item's icon are defined in the onCreatOptionsMenu method as follow:

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
     // getMenuInflater().inflate(R.menu.main, menu);
     this.menu = menu;
     if (mDrawerLayout != null && isDrawerOpen())
         showGlobalContextActionBar();
         MenuInflater menuInflater = this.getMenuInflater();
         menuInflater.inflate(R.menu.notification, menu);
         return super.onCreateOptionsMenu(menu);
     }
 }

and in the updateCount method, I am changing the icon as follow:

public void updateCount(int count) { 
    hot_count = count;
    System.out.println("Value of count: " + count);
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            if (hot_count > 0) {
                if(hot_count>0)
                    {
                    if (menu != null) {
                        MenuItem item = menu.findItem(R.id.menu_hotlist);
                        if (item != null) {
                            item.setIcon(R.drawable.ic_notification1);
                        }
                    }
                }
            }                   
        }
    });
}

Here is my menuitem "notification" file:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
   >

     <item android:id="@+id/menu_hotlist"
        android:actionLayout="@layout/action_bar_notification_icon"
        android:showAsAction="always"
        android:icon="@drawable/ic_notification"
        android:title="Notification" />

</menu>

Here's my logcat:

01-20 15:03:29.811: E/AndroidRuntime(10318): java.lang.NullPointerException
01-20 15:03:29.811: E/AndroidRuntime(10318):    at com.xsinfosol.helpdesk_customer.TAB_Activity$3.run(TAB_Activity.java:294)
01-20 15:03:29.811: E/AndroidRuntime(10318):    at android.os.Handler.handleCallback(Handler.java:730)
01-20 15:03:29.811: E/AndroidRuntime(10318):    at android.os.Handler.dispatchMessage(Handler.java:92)
01-20 15:03:29.811: E/AndroidRuntime(10318):    at android.os.Looper.loop(Looper.java:137)
01-20 15:03:29.811: E/AndroidRuntime(10318):    at android.os.HandlerThread.run(HandlerThread.java:61)
01-20 15:04:04.881: I/System.out(11629)

Please help.

Fellner answered 20/1, 2015 at 9:44 Comment(0)
O
10

Looks like menu.getItem(index) is returning null because menu was not inflated ( you have check mDrawerLayout != null && isDrawerOpen()) or you might have index that doesn't exists. Instead of relying on menu item index you can use resource id, also do check for null:

if (menu != null) {
    MenuItem item = menu.findItem(R.id.your_menu_action);
    if (item != null) {
        item.setIcon(R.drawable.ic_notification1);
    }
}

Update: based on you code i did example below that works. You can use it as base or for comparing to find why your code is not working. I don't know how @layout/action_bar_notification_icon looks like so in your case might be problem there.

In this example ic_menu_delete is replaced by ic_menu_edit once you click on menu item.

test_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/test_menu_item"
        android:icon="@android:drawable/ic_menu_delete"
        android:showAsAction="always"
        android:title="Item1"/>
</menu>

Code:

private Menu menu;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    this.menu = menu;
    getMenuInflater().inflate(R.menu.test_menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.test_menu_item:
        changeIcon();
        break;

    default:
        break;
    }
    return super.onOptionsItemSelected(item);
}

changeIcon() simulates your updateCount()

public void changeIcon(){
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if (menu != null) {
                MenuItem item = menu.findItem(R.id.test_menu_item);
                if (item != null) {
                    item.setIcon(android.R.drawable.ic_menu_edit);
                }
            }
        }
    });
}
Okeefe answered 20/1, 2015 at 11:21 Comment(9)
I implemented your code however it snot changing icon. Sir pleas tell me how to change icon.Fellner
Make sure that item.setIcon() is called. If it is not, check why.Okeefe
menu is returning nullFellner
Is menuInflater.inflate() called and you are passing correct resource?Okeefe
Update your initial post with updated code and attach also menu resource.Okeefe
I see now that in posted code in onCreateOptionsMenu() you have compilation error because boolean flag is only retuned in if statement. You probably want to return true instead of calling super here also.Okeefe
will you please elaborate.?Fellner
For test try with this: public boolean onCreateOptionsMenu(Menu menu) { this.menu = menu; getMenuInflater().inflate(R.menu.notification, menu); return true; }Okeefe
@ankur-arora Maybe you are calling in some place to invalidateOptionsMenu(). It could be restoring the icon to it's defaultDang
D
6

I've also had the same problem and @Dario answers works like a charm as long as you don't call invalidateOptionsMenu()

To solve this, I assign the drawable resource to a variable and call invalidateOptionsMenu() where I want to change the icon and I set the icon in onCreateOptionsMenu(). The code should be like this:

private int drawableResourceId = R.drawable.default_menu_icon;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.test_menu, menu);
    menu.findItem(R.id.change_menu_item_icon).setIcon(drawableResourceId);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.change_menu_item_icon:
        drawableResourceId = R.drawable.changed_menu_icon;
        invalidateOptionsMenu();
        break;

    default:
        break;
    }
    return super.onOptionsItemSelected(item);
}
Dang answered 10/3, 2016 at 8:41 Comment(2)
Excellent solution my friend! +1Sundial
1+ super classic !Minoru
D
0

I've had the same problem. Please make sure you have the following in your menu item (an icon and showAsAction set to always) :

android:icon="@android:drawable/ic_menu_delete" 
app:showAsAction="always"
Dieppe answered 9/12, 2015 at 15:49 Comment(0)
V
0

There are 3 steps:

  1. define a global MenuItem variable .
  2. in onCreateOptionsMenu method assign your value (the target menu) to it.
  3. change the icon when required.

     public class NotificationActivity extends BaseActivity {
      //#1
      Menu globalMenuItem;
      //other variables
    
       @Override
         public boolean onCreateOptionsMenu(Menu menu) {  
          getMenuInflater().inflate(R.menu.notification_menu, menu);
          //#2
          globalMenuItem= menu.add(Menu.NONE,menuId, Menu.NONE, title);
          globalMenuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
                  .setIcon(R.drawable.notification_icon_1);
          //other menu items
       }
    
       //#3  call this when required
       private void changeIcon(){
           globalMenuItem.setIcon(R.drawable.notification_icon_2);
       }
       //...
    }
    
Vestment answered 15/6, 2017 at 19:16 Comment(0)
A
0

As the docs state here, you should use onPrepareOptionsMenu() for any menu changes during the runtime of the Activity. It gets invoked whenever you call invalidateOptionsMenu().

Summary:

  1. Do the initialization of the menu in onCreateOptionsMenu()
  2. Whatever needs to be updated dynamically goes in onPrepareOptionsMenu()
  3. Call invalidateOptionsMenu() when you have detected a change and want to update the options menu
Acid answered 6/5, 2022 at 7:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.