Android MenuItem';s的选中状态未通过其图标正确显示

Android MenuItem';s的选中状态未通过其图标正确显示,android,menu,menuitem,android-actionbar,Android,Menu,Menuitem,Android Actionbar,我对MenuItem的定义如下: <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_starred" android:icon="@drawable/btn_star" android:title="@string

我对MenuItem的定义如下:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_starred"
        android:icon="@drawable/btn_star"
        android:title="@string/description_star"
        android:checkable="true"
        android:checked="true"
        android:orderInCategory="1"
        android:showAsAction="always" />
</menu>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item 
        android:state_checked="false" 
        android:drawable="@drawable/btn_star_off_normal" />
    <item 
        android:state_checked="true"
        android:drawable="@drawable/btn_star_on_normal" />
</selector>
但是,当我使用此选项创建选项菜单时,即使
MenuItem
isChecked()

我正在使用控件,但是,如果我只是创建一个普通选项菜单并调用
setChecked(true)
,我会得到相同的结果。无论项目的选中状态如何,它仍然显示可绘制的
btn\u star\u off

正在正确调用
OnOptions ItemSelected()
方法,并且我可以成功更改checked属性:

@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if(item.isCheckable()) {
            item.setChecked(!item.isChecked());
        }
        return super.onOptionsItemSelected(item);
}
在此处设置断点会显示正在更改的isChecked属性,但图标本身不会更新以反映正确的状态


这里有我遗漏的东西吗?我做得不对吗?我不明白为什么这不能正常工作。

根据

注意:图标菜单(从选项菜单)中的菜单项不能 显示复选框或单选按钮。如果您选择在 图标菜单可检查,您必须通过 每次状态更改时交换图标和/或文本。


希望它能有所帮助。

如果您仍然希望在xml可绘制文件中定义行为(选中,未选中),这是一种可以实现这一点的方法:

if (item.getItemId()==R.id.menu_item){
    item.setChecked(!item.isChecked());
    StateListDrawable stateListDrawable = (StateListDrawable) getResources().getDrawable(R.drawable.selector_drawable);
    int[] state = {item.isChecked()?android.R.attr.state_checked:android.R.attr.state_empty};
    stateListDrawable.setState(state);
    item.setIcon(stateListDrawable.getCurrent());
}
一种更简单的方法(没有xml状态文件):


//在java中。。。
@凌驾
公共布尔值onOptionsItemSelected(菜单项项){
开关(item.getItemId()){
案例R.id.fav:
布尔值mState=!item.isChecked();
项目设置已检查(mState);
item.setIcon(mState?getResources().getDrawable(R.drawable.ic_favorite_black_selected):getResources().getDrawable(R.drawable.ic_favorite_black_UnSelected));
Toast.makeText(this,“+item.isChecked(),Toast.LENGTH_SHORT.show();
返回true;
案例R.id.share:
返回true;
}
返回super.onOptionsItemSelected(项目);
}

这个问题有点老了,但我最近偶然发现了这个问题

经过一些分析,结果表明菜单项的选中状态并没有被正确地传播到可绘图项。以下是我(在科特林)提出的解决方案

确保您的
menuItem
正在使用可绘制的状态列表(如问题中的
btn_star.xml
),然后创建一个可绘制的包装类:

/** Fixes checked state being ignored by injecting checked state directly into drawable */
class CheckDrawableWrapper(val menuItem: MenuItem) : DrawableWrapper(menuItem.icon) {
    // inject checked state into drawable state set
    override fun setState(stateSet: IntArray) = super.setState(
        if (menuItem.isChecked) stateSet + android.R.attr.state_checked else stateSet
    )
}

/** Wrap icon drawable with [CheckDrawableWrapper]. */
fun MenuItem.fixCheckStateOnIcon() = apply { icon = CheckDrawableWrapper(this) }
最后一步是将菜单项drawable替换为wrapped drawable:

override fun onCreateOptionsMenu(menu: Menu) : Boolean{
    menuInflater.inflate(R.menu.menu_main, menu)
    menu.findItem(R.id.menu_starred).fixCheckStateOnIcon()
    /** ...  */
    return true
}

此后,当更改菜单项的选中状态时,您无需执行任何操作,图标应具有自我意识,并在选中状态更改时作出反应。

啊,我不知道我怎么会错过这一点--我阅读了几次该文档。谢谢让我困惑的是,添加
checkable=“true”
确实会在菜单项中添加一个复选框,而且这个警告似乎只适用于图标菜单中的菜单项,而不是所有菜单。谷歌为什么?为什么不能只使用绘图的state_checked?我发现同样的,出于某种原因,state_checked不适用于菜单图标什么是
R.drawable.selector_drawable
?包含多个状态(聚焦、按下等)的xml绘图。在上面的示例中,它将是
btn_star.xml
为什么变量
configChecked
是必需的,因为每次单击checkboxDrawableWrapper类需要API级别时,都必须将其更改为相反的值23@A.Petrov我认为您可以使用
androidx.appcompat.graphics.drawable.drawable包装器
,只需添加
@SuppressLint(“RestrictedApi”)
在类的顶部。
@SuppressLint
始终是一种糟糕的方法。目前,我最终得到了@Andrei Tudor Diaconu的解决方案,主要是:状态列表drawable可能包含许多状态(>2),但只有两个第一项状态受到影响,可以用于处理
工具栏。MenuItem
我认为这是否总是一种糟糕的方法。我猜他们把受限注释放在那个类的顶部,因为它只是一个“粗糙的助手类”,而不是一个公开的api,但我认为忽略它没有问题,因为它只是一个简单的对包装对象的委托(你可以检查源)。>(你可以检查源)-这是关键。目前的消息来源。无法保证在Jetpack的进化过程中该API能够继续存在。
/** Fixes checked state being ignored by injecting checked state directly into drawable */
class CheckDrawableWrapper(val menuItem: MenuItem) : DrawableWrapper(menuItem.icon) {
    // inject checked state into drawable state set
    override fun setState(stateSet: IntArray) = super.setState(
        if (menuItem.isChecked) stateSet + android.R.attr.state_checked else stateSet
    )
}

/** Wrap icon drawable with [CheckDrawableWrapper]. */
fun MenuItem.fixCheckStateOnIcon() = apply { icon = CheckDrawableWrapper(this) }
override fun onCreateOptionsMenu(menu: Menu) : Boolean{
    menuInflater.inflate(R.menu.menu_main, menu)
    menu.findItem(R.id.menu_starred).fixCheckStateOnIcon()
    /** ...  */
    return true
}