ankur arora
ankur arora

Reputation: 161

How to change the ActionBar menu item's icon dynamically

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.

Upvotes: 14

Views: 21522

Answers (5)

Leo
Leo

Reputation: 101

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

Upvotes: 0

sepehr
sepehr

Reputation: 897

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);
       }
       //...
    }
    

Upvotes: 0

Zinc
Zinc

Reputation: 1002

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);
}

Upvotes: 6

Ana_Maria
Ana_Maria

Reputation: 1

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"

Upvotes: 0

Dario
Dario

Reputation: 2073

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);
                }
            }
        }
    });
}

Upvotes: 10

Related Questions