Android可选菜单项

73

我在我的Android应用中有以下菜单布局:

<?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>

我的问题是,当我在Android模拟器中运行我的应用程序时,第二个菜单项似乎不能够被“勾选”。该项应该有一个绿色的勾号,对吗?以表示它是可勾选的。

我做错了什么吗?


2
你只需要将android:checkable设置为“true”,而不是android:checked! - Felix Julian
9个回答

88

布局看起来没问题。但你需要在代码中选中和取消选中菜单项。

根据文档

当选择一个可选项目时,系统会调用相应的项目选择回调方法(例如onOptionsItemSelected())。在这里,你必须设置复选框的状态,因为复选框或单选按钮不会自动更改状态。你可以使用isChecked()查询项目的当前状态(在用户选择之前的状态),然后使用setChecked()设置选中状态。


在这个引用上面,它说“您可以使用android:checked属性在<item>元素中应用默认选中状态,并使用setChecked()方法在代码中更改它。”哪一个是正确的? - Gabriel Negut
1
我看不出有什么问题。在这个引用中说可能应用默认的检查状态,并且检查状态可以被改变。而在我的引用中说,需要通过代码手动更改检查状态。 - Sergey Glotov
1
这个是我的错..我被菜单项的初始状态搞混了,不知道是否需要从代码中设置。看起来初始选中状态可以从xml中设置,并且之后可以在代码中随意更改,是吗? - Gabriel Negut
是的,我曾经应用Java代码来编程地检查和取消选中项目,并持久化状态,这对我起了作用,使得Sergey成为赢家。谢谢! - Icemanind
谢谢您的建议。我们如何在勾选后保持菜单打开? - Srikar Reddy
请注意,他们在引用下面提供的示例代码时,该代码无法正常工作。 - The Berga

43

item 包裹在一个 group 元素中,像这样:

<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>

Android文档中得知:

android:checkableBehavior属性只接受以下三种值:

single - 组中只能选中一个项目(单选按钮)

all - 所有项目都可以被选中(复选框)

none - 没有任何项目可选中


你的代码中只有一个项目使用了 android:checkable="true"android:icon 不适用于可选项目。 - user924

27

actionViewClass设为可选中小部件(例如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>

你可以把actionLayout设为一个带有样式化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>

为设定数值
menuItem.setChecked(true/false);

获取值

menuItem.isChecked()

将 MenuItem 转换为 CheckBox
CheckBox checkBox= (CheckBox) menuItem.getActionView();

终于,工作的代码!!我花了两个小时才找到可行的方法!菜单不应该这么难啊!!在菜单中使用复选框时,app:actionViewClass 是关键。 - Someone Somewhere
4
结果表明这实际上并不好,因为复选框不是菜单项的一部分;用户会点击文字,而复选框则完全没动静 - 虽然我可以通过编程来改变这个问题。然而,当用户仅点击复选框时,导航选择事件(onNavigationItemSelected())却无法触发。 - Someone Somewhere
目前我无法推荐在导航抽屉中使用菜单,如果你想要复选框的话。这会出现各种问题!我将手动在菜单下添加复选框。 - Someone Somewhere
我在菜单消失时添加了300毫秒的“隐藏”中断延迟(仅当他们点击复选框时)。我会找出代码,看起来还不错。 - Mr Heelis

4

我发现在当前的 API(27-28)中,最好的解决方案是只使用 onOptionsItemSelected() 方法。

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

我在这里花了太长时间来回答这个问题。由于某些原因,上面的答案没有帮助(我是一个重返团队的新手,我肯定搞砸了某些东西)。可能有更好的方法来解决这个问题,欢迎提供有用的批评建议。

1

请注意

正如所说的,“手动检查”只是冰山一角。它会非常快地闪现菜单,用户看不到任何变化,这非常反直觉、令人沮丧,实际上是一团糟。因此,真正的任务是让复选框事件被用户的大脑消化。

好消息是:这是可以做到的,并且它确实有效,以下是操作步骤。@TouchBoarder提供了最佳代码,我将复制他的代码,然后进行开发。

思路是检测复选框是否被点击,然后(仅在选择该复选框时)轻微抑制菜单的移除,添加一个500ms的计时器,然后关闭菜单,这样可以给复选框的“打勾”动画留出时间运行并创造正确的“感觉”。

<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>

然后您按照通常的方式编写此方法,但确保添加所有这些额外的冗长说明。
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;
}

其他菜单事件在这里。
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

回答这个问题是因为这里的回答看起来很长而且复杂。我这里有一些精确的Kotlin代码。

在顶部覆盖你的activity并重写onMenuItemClick函数,有一个函数来处理按钮点击打开菜单。

有一个数组或列表来保存选中的值,并在重新创建菜单时设置选中状态。

注意:这段代码不会保持菜单打开,它只能确保选中的项目保持选中状态。我注意到在stackoverflow上有很多解决方案,所以如果你需要,请去看看它们。

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

当然,在XML中,您应该设置按钮和菜单。这里有一个示例菜单。
<?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

要通过编程方式添加菜单项,

@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

这可能与主题有关,但是我的菜单没有显示复选框。我找到了this

注意:图标菜单中的菜单项无法显示复选框或单选按钮。如果您选择使图标菜单中的项目可勾选,则必须通过交换图标和/或文本来手动指示每次状态在打开和关闭之间更改时的状态。


0
我在菜单中有两个项目,并在menu.xml文件中设置为可选,如下所示:
    <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"/> 

菜单复选框的逻辑如下。

@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
你可以简化它:item.setChecked(!item.isChecked()) - Ridcully

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接