MenuItem's checked state is not shown correctly by its icon

℡╲_俬逩灬. 提交于 2019-12-17 15:56:15

问题


I have MenuItem defined this way:

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

and btn_star.xml defined this way:

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

When I create an options menu using this, however, the icon is never shown in its checked state, even if the MenuItem's isChecked() property is true.

I'm using the ActionBarSherlock control, however, I'm getting the same result if I simply create a normal options menu and call setChecked(true). It still displays the btn_star_off drawable regardless of the checked state of the item.

The onOptionsItemSelected() method is being called correctly, and I can successfully change the checked property:

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

Setting a breakpoint here shows the isChecked property being changed, but the icon itself is not updated to reflect the correct state.

Is there something I'm missing here? Am I doing this incorrectly? I can't figure out why this wouldn't be working correctly.


回答1:


According to the official document at http://developer.android.com/guide/topics/ui/menus.html

Note: Menu items in the Icon Menu (from the Options Menu) cannot display a checkbox or radio button. If you choose to make items in the Icon Menu checkable, you must manually indicate the checked state by swapping the icon and/or text each time the state changes.

Hope it helps.




回答2:


If you still want to have the behavior (checked, not checked) defined in a xml drawable, this is one way you could accomplish this:

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



回答3:


A bit simpler way (without xml-states file):

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



回答4:


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



回答5:


Question is a bit old but I stumbled upon this problem recently.

After some analyzing it turns out that checked state of menu item is not being properly propagated down to the drawable. Here's the solution I came up with (in Kotlin).

Ensure your menuItem is using a state list drawable (like btn_star.xml from question), then create a drawable wrapper class:

/** 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) }

Last step is replacing menu items drawable with wrapped drawable:

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

After that you don't need to do anything when changing menu items checked state, icon should be self aware and react whenever checked state changes.



来源:https://stackoverflow.com/questions/6683186/menuitems-checked-state-is-not-shown-correctly-by-its-icon

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!