73

I have the following menu layout in my Android app:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/item1" 
          android:titleCondensed="Options"
          android:title="Highlight Options" 
          android:icon="@android:drawable/ic_menu_preferences" />

   <item android:id="@+id/item2" 
         android:titleCondensed="Persist"
         android:title="Persist" 
         android:icon="@android:drawable/ic_menu_preferences" 
         android:checkable="true" />
</menu>

My problem is that the second menu item doesn't appear to be "checkable" when I run my app in the Android emulator. There should be a green tick about the item, right? To indicate that its checkable.

Am I doing something wrong?

1
  • 2
    You just need to set not android:checkable to "true", but android:checked ! Commented May 24, 2015 at 9:35

9 Answers 9

88

Layout looks right. But you must check and uncheck menu item in code.

From the documentation:

When a checkable item is selected, the system calls your respective item-selected callback method (such as onOptionsItemSelected()). It is here that you must set the state of the checkbox, because a checkbox or radio button does not change its state automatically. You can query the current state of the item (as it was before the user selected it) with isChecked() and then set the checked state with setChecked().

6
  • Right above this quote it says You can apply a default checked state to an item using the android:checked attribute in the <item> element and change it in code with the setChecked() method. Which one is correct? Commented Jun 4, 2011 at 20:36
  • 1
    I don't see what is wrong. In this quote said that it may be applied default check state and check state can be changed. In my quote said that check state need to change manually by code. Commented Jun 4, 2011 at 20:41
  • 1
    My bad on this.. I was confused by the initial state of the menu items, if it is necessary to be set from code or not. It looks like the initial checked state can be set from xml, and after can be altered at will from code, is it correct? Commented Jun 4, 2011 at 20:49
  • Yes, once I applied Java code to programmically check and uncheck the item, and persisted the state, it worked for me, which makes Sergey the winner. Thanks!
    – Icemanind
    Commented Jun 4, 2011 at 21:14
  • Thanks for the suggestion. How can we keep the menu open after checking it? Commented Aug 29, 2015 at 9:49
43

Wrap the items in a group element, like this:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="all">
        <item android:id="@+id/item1"
              android:titleCondensed="Options"
              android:title="Highlight Options"
              android:icon="@android:drawable/ic_menu_preferences">
        </item>
        <item android:id="@+id/item2"
              android:titleCondensed="Persist"
              android:title="Persist"
              android:icon="@android:drawable/ic_menu_preferences"
              android:checkable="true">
        </item>
    </group>
</menu>

From the Android docs:

The android:checkableBehavior attribute accepts either:

single - Only one item from the group can be checked (radio buttons)

all - All items can be checked (checkboxes)

none - No items are checkable

1
  • your code has android:checkable="true" only for one item, android:icon is not for checkable items
    – user924
    Commented Dec 10, 2021 at 9:21
30

You can create a checkable menu item by setting the actionViewClass to a checkable widget like android.widget.CheckBox

res/menu/menu_with_checkable_menu_item.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_favorite"
        android:checkable="true"
        android:title="@string/action_favorite"
        app:actionViewClass="android.widget.CheckBox"
        app:showAsAction="ifRoom|withText" />
</menu>

And you can can even style it to be a checkable star if you set actionLayout to a layout with a styled android.widget.CheckBox

res/layout /action_layout_styled_checkbox.xml

<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
    style="?android:attr/starStyle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

res/menu/menu_with_checkable_star_menu_item.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_favorites"
        android:checkable="true"
        android:title="@string/action_favorites"
        app:actionLayout="@layout/action_layout_styled_checkbox"
        app:showAsAction="ifRoom|withText" />
</menu>

To set the value

menuItem.setChecked(true/false);

To get the value

menuItem.isChecked()

Cast MenuItem to CheckBox

CheckBox checkBox= (CheckBox) menuItem.getActionView();
4
  • finally - working code!! it's taken me two hours to find something that works! Menu's shouldn't be this damn hard !! app:actionViewClass is where it's at for checkboxes in a menu Commented Apr 23, 2019 at 0:13
  • 4
    it turns out that this is actually not good, because the checkbox is not part of the menu item; the user will click the text and the checkbox remains untouched - granted I can change this programmatically. HOWEVER, when the user touches just the checkbox then the damn onNavigationItemSelected() isn't triggered. Commented Apr 23, 2019 at 20:23
  • at this point I can't recommend usage of menus in the navigation drawer - if you want checkboxes. It's all kinds of broke ! I'm going to add checkboxes manually under the menu. Commented Apr 23, 2019 at 20:26
  • I added a 300ms hide interrupt delay on the menu disappearing (only if they clicked the check box).. I'll dig out the code.. it looks ok
    – Mr Heelis
    Commented Aug 30, 2019 at 9:11
4

I've found that the best solution was to just use the onOptionsItemSelected() method as of my current API (27-28).

@Override
   public boolean onOptionsItemSelected(MenuItem item) 
   {    

//Copy from here...
       int itemId = item.getItemId();

       if(item.isChecked())                          
       { 
           if(R.id.edit_tile_checkbox == itemId)     //Individual checkbox logic
           {   /*TODO unchecked Action*/} 
           item.setChecked(false);                   //Toggles checkbox state.
       }
       else
       {
            if(R.id.edit_tile_checkbox == itemId)    //Individual checkbox logic
            {/*TODO checked Action*/}
            item.setChecked(true);                   //Toggles checkbox state.
       }
//...To here in to your onOptionsItemSelected() method, then make sure your variables are all sweet.

       return super.onOptionsItemSelected(item);
   }

I spent way to long on here for this answer. and for whatever reason, the answers above didn't help (I'm a returning newbie I probably mucked something up I'm sure). There could be a better way of doing this so helpful criticism is welcomed.

1

READ THIS

As has been said the "manual checking" is only the tip of the iceberg. It flashes the menu away so fast the users don't see anything happen and it is very counter intuitive, frustrating, and effectively utter crap. The REAL TASK (therefore) is allowing the check box event to be digested by the users mind.

Good news: this can be done and it does work and this is how you do it. @TouchBoarder had it best so I will copy his code. then develop it.

the idea is to detect if the checkbox is clicked, then (and only if that one is picked) slightly suppress the menu removal, add a timer for 500ms then close the menu, this give the "tick" animation of the checkbox time to run and creates the right "feel"

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_favorite"
        android:checkable="true"
        android:title="@string/action_favorite"
        app:actionViewClass="android.widget.CheckBox"
        app:showAsAction="ifRoom|withText" />
</menu>

then you make this method as usual, but you make sure you add all this extra bumpf

public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the bottom bar and the top bar (weird)
    BottomAppBar bottomBar = findViewById(R.id.bottom_app_bar_help);
    Menu bottomMenu = bottomBar.getMenu();
    getMenuInflater().inflate(R.menu.bottom_nav_menu, bottomMenu);
    for (int i = 0; i < bottomMenu.size(); i++) {
        bottomMenu.getItem(i).setOnMenuItemClickListener(item -> {
            if (item.getItemId()==R.id.action_favorite){
                item.setChecked(!item.isChecked());
                // Keep the popup menu open
                item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
                item.setActionView(new View(frmMain.this));
                item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
                    @Override
                    public boolean onMenuItemActionExpand(MenuItem item) {
                        final Handler handler = new Handler();
                        handler.postDelayed(() -> bottomMenu.close(), 500);
                        return false;
                    }

                    @Override
                    public boolean onMenuItemActionCollapse(MenuItem item) {
                        final Handler handler = new Handler();
                        handler.postDelayed(() -> bottomMenu.close(), 500);
                        return false;
                    }
                });
                return false;
            }
            else {
                return onOptionsItemSelected(item);
            }
        });
    }
    return true;
}

the other menu events are here

public boolean onOptionsItemSelected(MenuItem item) {
    // Bottom Bar item click
    try {
        switch (item.getItemId()) {
            case R.id.mnuExit:
                MenuClick(ClickType.LOGOUT);
                return true;

            case R.id.mnuList:
                MenuClick(ClickType.LIST);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return super.onOptionsItemSelected(item);
}
1

Answering because the answers here seem long and convoluted.. I have some exact Kotlin code here

Override your activity at the top and override the function onMenuItemClick, Have a function to handle the button click to open the menu.

Have an array or list which holds the checked value and sets the check when the menu is re-created

Note: This code does not keep the menu open, It only ensures that checked items remain checked. I noted there are lots of solutions to that on stack overflow, so have a look at them if that's what you desire

class exampleActivity : AppCompatActivity(), PopupMenu.OnMenuItemClickListener {
   private var checkChecked = arrayListOf(false,false)
   //some code

  fun clickBTN(v: View){
        val popup = PopupMenu(this,v)
        popup.setOnMenuItemClickListener(this)
        popup.inflate(R.menu.yourmenufilename)
        //assuming you have 2 or more menu items
        popup.menu[0].isChecked = checkChecked[0]
        popup.menu[1].isChecked = checkChecked[1]
        popup.show()
  }

  override fun onMenuItemClick(item: MenuItem?): Boolean {
     when(item?.itemID){
        R.id.item0 -> {
                item.isChecked = !item.isChecked
                checkChecked[0] = item.isChecked
                return true
        }
        R.id.item1 -> {
                item.isChecked = !item.isChecked
                checkChecked[1] = item.isChecked
                return true
        }
  }
}

of course in XML you should have your Button and Menu setup. An example menu is here

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/item0"
        android:title="@string/hi"
        android:checkable="true"/>
    <item android:id="@+id/item1"
        android:title="@string/yo"
        android:checkable="true"/>
</menu>
0

This may be theme dependant but my menu didn't show a checkbox. I found this :

Note: Menu items in the Icon Menu cannot display a checkbox or radio button. If you choose to make items in the Icon Menu checkable, then you must personally indicate the state by swapping the icon and/or text each time the state changes between on and off.

0

For Adding Menu items Programmatically,

@Override
public boolean onCreateOptionsMenu(Menu menu) {

    menu.add("Item1").setActionView(R.layout.action_layout_checkbox).setCheckable(true);
    return super.onCreateOptionsMenu(menu);
}

res/layout /action_layout_checkbox.xml

<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
0

I have two items in the menu and set to checkable in menu.xml file like below

    <item
        android:id="@+id/A"
        android:title="A"
        app:showAsAction="never"
        android:checkable="true"/>
    <item
        android:id="@+id/B"
        android:title="B"
        app:showAsAction="never"
        android:checkable="true"/> 

and logic for the menu checkboxes is below.

@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {

    switch (item.getItemId()) {
        case R.id.A:
           //logic goes here


            if(item.isChecked())
            {
             //logic is it is checked
             item.setChecked(false);
            }
            else
            {
               //logic is it is not checked
                item.setChecked(true);
            }
            return true;
        case R.id.B:
         //logic for second checkbox goes here


            if(item.isChecked())
            {
             //logic is it is checked
                item.setChecked(false);
            }
            else
            {
             //logic is it is not checked
                item.setChecked(true);
            }
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
1
  • 1
    You can simplify that: item.setChecked(!item.isChecked())
    – Ridcully
    Commented Jun 28, 2022 at 10:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.