【问题标题】:MenuItem's checked state is not shown correctly by its iconMenuItem 的选中状态未通过其图标正确显示
【发布时间】:2011-10-04 17:17:36
【问题描述】:

我以这种方式定义了 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>

btn_star.xml 这样定义:

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

但是,当我使用它创建选项菜单时,图标永远不会显示为选中状态,即使 MenuItemisChecked() 属性为 true。

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

onOptionsItemSelected() 方法被正确调用,我可以成功更改选中的属性:

@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if(item.isCheckable()) {
            item.setChecked(!item.isChecked());
        }
        return super.onOptionsItemSelected(item);
}

在此处设置断点显示 isChecked 属性正在更改,但图标本身并未更新以反映正确的状态。

这里有什么我遗漏的吗?我做错了吗?我不明白为什么这不能正常工作。

【问题讨论】:

  • 我也发现了,由于某种原因,state_checked 不适用于菜单图标

标签: android menu menuitem android-actionbar


【解决方案1】:

根据http://developer.android.com/guide/topics/ui/menus.html的官方文档

注意:图标菜单中的菜单项(来自选项菜单)不能 显示复选框或单选按钮。如果您选择在 图标菜单可勾选,您必须手动指示勾选状态 每次状态改变时交换图标和/或文本。

希望对你有帮助。

【讨论】:

  • 啊,我不知道我是怎么错过的——我通读了几次该文档。谢谢!
  • 令我困惑的是,添加checkable="true" 确实会为菜单项添加一个复选框,而且这个警告似乎只适用于图标菜单中的菜单项,而不是所有菜单。跨度>
  • 谷歌为什么?为什么不能只使用 drawable 的 state_checked?
【解决方案2】:

如果您仍然希望在 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());
}

【讨论】:

  • 什么是R.drawable.selector_drawable
  • 包含多种状态(聚焦、按下等)的 xml 可绘制对象。在上面的示例中,它将是 btn_star.xml
【解决方案3】:

更简单一点的方法(没有 xml-states 文件):

configChecked = !configChecked;
item.setChecked(configChecked);
item.setIcon(configChecked ? R.drawable.check_on : R.drawable.check_off);

【讨论】:

  • 为什么需要变量configChecked
  • Flimm,因为每次单击复选框时都必须将其更改为相反的值
【解决方案4】:

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

经过一些分析,结果发现菜单项的选中状态没有正确传播到可绘制对象。这是我想出的解决方案(在 Kotlin 中)。

确保您的 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替换菜单项drawable:

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

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

【讨论】:

  • DrawableWrapper 类需要 API 级别 23
  • @A.Petrov 我认为您可以改用androidx.appcompat.graphics.drawable.DrawableWrapper,只需在类顶部添加@SuppressLint("RestrictedApi")
  • @SuppressLint 总是一个不好的方法。目前,我最终得到了@Andrei Tudor Diaconu 的解决方案,主要说明:状态列表drawable 可能包含许多状态(> 2)但只有两个第一个项目状态受到影响,可以用于与@987654328 的交易@
  • 我会争论这是否总是一种不好的方法。我猜他们在该类之上放置了受限注释,因为它只是一个“粗略的帮助类”而不是暴露的 api,但我没有看到忽略它的问题,因为它只是一个简单的包装对象委托(你可以检查来源)。
  • >(您可以查看源代码)- 这是关键。即时消息来源。在 Jetpack 的演变过程中,无法保证此 API 能够继续存在。
【解决方案5】:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/fav"
        android:title=""
        app:showAsAction="ifRoom"
        android:orderInCategory="1"
        android:icon="@drawable/ic_favorite_black_unselectd"
        android:checked="false" />
    <item android:id="@+id/share"
        android:title=""
        app:showAsAction="ifRoom"
        android:orderInCategory="2"
        android:icon="@drawable/ic_share_black" />
</menu>

//and in java...

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.fav:
            boolean mState = !item.isChecked();
            item.setChecked(mState);
            item.setIcon(mState ? getResources().getDrawable(R.drawable.ic_favorite_black_selected) : getResources().getDrawable(R.drawable.ic_favorite_black_unselectd));
            Toast.makeText(this, "" + item.isChecked(), Toast.LENGTH_SHORT).show();
            return true;
        case R.id.share:
            return true;
    }
    return super.onOptionsItemSelected(item);
}

【讨论】:

    猜你喜欢
    • 2018-06-12
    • 1970-01-01
    • 2021-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多