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.
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);
}
}
}
});
}
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);
}
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"
There are 3 steps:
define a global MenuItem variable .
in onCreateOptionsMenu method assign your value (the target
menu) to it.
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);
}
//...
}
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:
Do the initialization of the menu in onCreateOptionsMenu()
Whatever needs to be updated dynamically goes in onPrepareOptionsMenu()
Call invalidateOptionsMenu() when you have detected a change and want to update the options menu
Related
This question already has answers here:
How to get an MenuItem by id
(3 answers)
Closed 3 years ago.
Background: I am trying to have an edit "button" (that is, a menu item) in the Action Bar, which would toggle between a TextView and an EditText. I got that working. Now, I'm trying to make the text in the button change to "save" after it is clicked.
The Problem: I cannot findById the menu item - it returns null. Could anyone tell me what am I doing wrong?
Here is the menu:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/itEdit"
android:title="EDIT"
app:showAsAction="always"
/>
</menu>
Here is the menu setup (i.e. extending the action bar)
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_written_note, menu);
return true;
}
#Override
public boolean onMenuOpened(int featureId, Menu menu){
// A global variable s.t. I could refer to it and update the text
itEdit = findViewById(R.id.itEdit);
return true;
}
Here's the toggling part:
public void toggleEdit() {
if(isEditing) {
// Editing -> Viewing
// Toggling TextView and EditText
if(itEdit != null) { // It is always null
itEdit.setTitle("EDIT");
}
} else {
// Viewing -> Editing
// Toggling TextView and EditText
if(itEdit != null) { // It is always null
itEdit.setTitle("SAVE");
}
}
isEditing = !isEditing;
}
You are referencing the menu item from the wrong place. The following solution should work.
public class TestActivity extends AppCompatActivity {
private Menu menu;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_written_note, menu);
this.menu = menu;
return true;
}
private void updateMenuTitle() {
MenuItem item = menu.findItem(R.id.itEdit);
item.setTitle("Test");
}
}
I am having list of colleges. If I click one of the college it has go to detail page which has a menu bar with favourite_icon.If I click the favourite_icon that college is stored as favourite in server and that favourite_icon changed as favourite_icon1.If we chech the college detail again after some times that menu bar favourite_icon should be favourite_icon1 if that college is already favoiurited.I am having the API to check whether the college is favourited or not if it favourited the response is like "status=fav" otherwise "status=not_fav".
Here I have added the code for your reference
menu_clg.xml
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:appmunu="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".UserDashBoardFragment">
<item
android:id="#+id/action_notify"
android:icon="#drawable/mail_icon"
appmunu:showAsAction="always"
android:title="Notification" />
<item
android:id="#+id/action_favourite"
android:icon="#drawable/icon_selector"
appmunu:showAsAction="always"
android:title="Favourite" />
</menu>
icon_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="true"
android:drawable="#drawable/vijay"
/>
<item
android:state_selected="false"
android:drawable="#drawable/favourite_icon"
/>
</selector>
activity code
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_clg, menu);
mMenu = menu;
return true;
}
// delete the selected event from event list added here
boolean canAddItem = false;
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_notify:
navigatetoNotification();
return true;
case R.id.action_favourite:
if(item.getItemId() == R.id.action_favourite){
invalidateOptionsMenu();
favouriteClg();
}
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
if(canAddItem){
menu.getItem(1).setIcon(R.drawable.vijay);
canAddItem = false;
favouriteClg();
}
else{
menu.getItem(1).setIcon(R.drawable.favourite_icon);
canAddItem = true;
favouriteClg();
}
return super.onPrepareOptionsMenu(menu);
}
favourtieclg() method
public void favouriteClg() {
final CollegeMO collegeMO = (CollegeMO) getIntent().getSerializableExtra("CollegeMO");
DatabaseHelper db = new DatabaseHelper(context);
userMO = db.getUserData(1);
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... arg0) {
return favouriteDelegates.addFavourite(userMO, collegeMO, context);
}
#Override
protected void onPostExecute(String userData) {
if (!userData.equals("0") && null != userData) {
UserMO userMO = gson.fromJson(userData, new TypeToken<UserMO>() {
}.getType());
if (userMO.getStatus().equals("success")) {
Toast.makeText(getApplicationContext(), userMO.getMessage(), Toast.LENGTH_LONG).show();
}
}
}
}.execute(null, null, null);
}
This is how i achieved it, hope this help. You can also use supportInvalidateOptionsMenu() or InvalidateOptionsMenu() to update changes.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
menu.clear(); //If you want to clear the menu
inflater.inflate(R.menu.activity_menu, menu);
updateMenuIcon(menu);
}
private void updateMenuIcon(Menu menu) {
if (menu == null) return;
if (condition) {//Check for some condition
changeMenuIcon(menu, R.drawable.fav);
} else {
changeMenuIcon(menu, R.drawable.not_fav);
}
}
private void changeMenuIcon(Menu menu, int resource) {
Drawable newIcon = getResources().getDrawable(resource);
menu.findItem(R.id.menu_fav).setIcon(newIcon);
}
I am having list of Colleges to display and click the perticular college it goes to the detail page. In there there is a menu bar with notification and Favourite_icon.Here i am having the Favourite_icon and Favourite_icon1. If users clcik the favourite_icon it stored as favourited in server and the icon changed as Favourite_icon1. After do some process i have visted the Favourited college. That time it should show the Favourite_icon1 in menu bar. I have tried the following method but nothing happend . I have added the the code which i have tried
menu_clg.xml
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:appmunu="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".UserDashBoardFragment">
<item
android:id="#+id/action_notify"
android:icon="#drawable/mail_icon"
appmunu:showAsAction="always"
android:title="Notification" />
<item
android:id="#+id/action_favourite"
android:icon="#drawable/icon_selector"
appmunu:showAsAction="always"
android:title="Favourite" />
</menu>
this Activity code
private boolean canAddItem;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_clg, menu);
mMenu = menu;
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_notify:
navigatetoNotification();
return true;
case R.id.action_favourite:
if (item.getItemId() == R.id.action_favourite) {
invalidateOptionsMenu();
favouriteClg();
}
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (canAddItem) {
menu.getItem(1).setIcon(R.drawable.vijay);
canAddItem = false;
favouriteClg();
} else {
menu.getItem(1).setIcon(R.drawable.favourite_icon);
canAddItem = true;
favouriteClg();
}
return super.onPrepareOptionsMenu(menu);
}
this is the code for check wheather the college is already favourited or not in onCreate() method
public void chechFavourite() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... arg0) {
//method name changed here
//getAllEventFromUser method used for getting all previously send events of current user
return favouriteDelegates.getAllCollegeDetails(userMO, context);
}
#Override
protected void onPostExecute(String collegelists) {
if (collegelists != "null") {
initCollegeMO = gson.fromJson(collegelists, new TypeToken<InitCollegeMO>() {
}.getType());
collegeMOs = initCollegeMO.getCollegeMOs();
for (CollegeMO collegeMO1 : collegeMOs) {
//here the list of college has eceived from server so i checked all the college id with current college id
collegeId = collegeMO1.getCollegeId(); //here collegeMO isthe object which is accessed by parcelable from another activity
if (collegeMO.getCollegeId() == collegeId) {
canAddItem = true;
} else {
canAddItem = false;
}
}
} else {
canAddItem = false;
}
}
}.execute(null, null, null);
}
}
Hope below code would be helpful for you.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate( R.menu.actionbar, menu );
userItem = menu.findItem(R.id.userItem);
return menu;
}
Edit 1:
Method which will update menu programatically.
private void updateMenuTitles() {
if(isFavorite){
userItem.setIcon(new BitmapDrawable(getResources(), favoriteBitmap));
}else{
userItem.setIcon(new BitmapDrawable(getResources(), unFavoriteBitmap));
}
invalidateOptionsMenu();
}
Hope this would help you.
You can change the title of your MenuItem runtime and manage the click event according to their title.
See this code, hope it will help you.
You can call updateMenuTitles() after getting response from API and change the title.
private Menu menu;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
// Create your menu...
this.menu = menu;
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_favourite:
if (item.getTitle().equals("favourite")) {
//Alerady Favourite, make it unfavourite
MenuItem.setTitle("un-favourite");
// do what you want
}
else if(item.getTitle().equals("un-favourite")){
// un-favourite, make it favourite
MenuItem.setTitle("favourite");
// do what you want
}
return true;
}
return super.onOptionsItemSelected(item);
}
/*
Update title of your menuItem whenever you want
*/
private void updateMenuTitles() {
MenuItem MenuItem = menu.findItem(R.id.action_favourite);
if (MenuItem.getTitle().equals("favourite")) {
//Alerady Favourite, make it unfavourite
MenuItem.setTitle("un-favourite");
}
else if(MenuItem.getTitle().equals("un-favourite")){
// un-favourite, make it favourite
MenuItem.setTitle("favourite");
}
}
I have a menu which is inflated from main_menu.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/act_sync"
android:showAsAction="always"
android:actionLayout="#layout/sync_action"
android:icon="#android:drawable/ic_popup_sync" />
</menu>
and here is the code in the activity:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
MyMessageHandler.debug("menu item selected");
switch(item.getItemId()){
case R.id.act_sync:
sync();
return true;
}
return super.onOptionsItemSelected(item);
}
But onOptionsItemSelected is not called when I touch the menu item. When I remove the actionLayout attribute of the menu item, it works fine. How can I fix this? Thanks.
you should use below snippet ( Just for reference )
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
final Menu m = menu;
final MenuItem item = menu.findItem(R.id.ActionConnection);
item.getActionView().setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sync();
}
});
return true;
}
In addition to the answer provided by Vipul Shah.
Make sure you disable the clickable option of all items in your action layout:
android:clickable="false"
Otherwise, it may steal the click so that you still cant receive onClick call back.
In Android if the textview is selected then a word gets selected and a contextual action bar comes at the top...i want to modify that CAB and make it look like a quick action bar...keeping the text selection feature intact...please help me...
you can select textview or anything like only in simple layout CAB appear in the top and there was some method onActionItemClicke that you want to click an item and then you modify your action in CAB default behavior................................
public class MainActivity extends Activity {
protected Object mActionMode;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View view = findViewById(R.id.lay);
view.setOnLongClickListener(new View.OnLongClickListener() {
// Called when the user long-clicks on someView
public boolean onLongClick(View view) {
if (mActionMode != null) {
return false;
}
mActionMode = MainActivity.this.startActionMode(mActionModeCallback);
view.setSelected(true);
return true;
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(this, "Just a test", Toast.LENGTH_SHORT).show();
return true;
}
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
// Called when the action mode is created; startActionMode() was called
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
// Assumes that you have "contexual.xml" menu resources
inflater.inflate(R.menu.cab, menu);
return true;
}
// Called each time the action mode is shown. Always called after
// onCreateActionMode, but
// may be called multiple times if the mode is invalidated.
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false; // Return false if nothing is done
}
// Called when the user selects a contextual menu item
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.toast:
Toast.makeText(MainActivity.this, "Selected menu",
Toast.LENGTH_LONG).show();
mode.finish(); // Action picked, so close the CAB
return true;
case R.id.shan:
Toast.makeText(MainActivity.this, "Selected shani",
Toast.LENGTH_LONG).show();
mode.finish(); // Action picked, so close the CAB
return true;
default:
return false;
}
}
// Called when the user exits the action mode
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
};
}
and in menu xml file create menu that you would like to appear in top CAB
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/toast"
android:title="Toast">
</item>
<item
android:id="#+id/shan"
android:title="shani">
</item>
</menu>