Проверенное состояние MenuItem неправильно отображается его значком

Я определил 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>

Однако, когда я создаю меню параметров, используя это, значок никогда не отображается в отмеченном состоянии, даже если свойство MenuItem isChecked() истинно.

Я использую элемент управления 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 изменяется, но сам значок не обновляется, чтобы отражать правильное состояние.

Есть ли что-то, что мне здесь не хватает? Я делаю это неправильно? Я не могу понять, почему это не будет работать правильно.


person Joe Krill    schedule 13.07.2011    source источник
comment
Я нашел то же самое, state_checked почему-то не работает для значков меню   -  person gregm    schedule 23.03.2012


Ответы (4)


Согласно официальному документу на http://developer.android.com/guide/topics/ui/menus.html

Примечание. Элементы меню в меню значков (из меню параметров) не могут отображать флажок или переключатель. Если вы решите сделать элементы в меню значков доступными для проверки, вы должны вручную указать отмеченное состояние, меняя местами значок и/или текст при каждом изменении состояния.

Надеюсь, поможет.

person Gaurav Vaish    schedule 18.08.2011
comment
Ах, я не знаю, как я это пропустил — я прочитал эту документацию несколько раз. Спасибо! - person Joe Krill; 19.08.2011
comment
Меня сбивает с толку то, что добавление checkable="true" действительно добавляет флажок к пункту меню, и что это предостережение, похоже, применяется только к пунктам меню в меню значков, а не ко всем меню. - person Flimm; 19.08.2016
comment
Гугл ЗАЧЕМ? Почему вы не можете просто использовать state_checked для рисования? - person DrBreakalot; 28.05.2019

Если вы все еще хотите, чтобы поведение (проверено, не проверено) было определено в 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());
}
person Andrei Tudor Diaconu    schedule 21.01.2014
comment
Что такое R.drawable.selector_drawable? - person Flimm; 19.08.2016
comment
Рисуемый xml, который содержит несколько состояний (сфокусировано, нажато и т. д.). В приведенном выше примере это будет btn_star.xml - person Andrei Tudor Diaconu; 19.08.2016

Немного проще (без файла xml-states):

configChecked = !configChecked;
item.setChecked(configChecked);
item.setIcon(configChecked ? R.drawable.check_on : R.drawable.check_off);
person kolyaseg    schedule 14.11.2014
comment
Зачем нужна переменная configChecked? - person Flimm; 19.08.2016
comment
Flimm, потому что вам нужно менять его на противоположное значение каждый раз, когда вы устанавливаете флажок - person kolyaseg; 12.10.2016

Вопрос немного устарел, но недавно я наткнулся на эту проблему.

После некоторого анализа выясняется, что проверенное состояние пункта меню не распространяется должным образом на рисуемый объект. Вот решение, которое я придумал (в Котлине).

Убедитесь, что ваш 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) }

Последний шаг — замена элементов меню, которые можно рисовать, на обернутые:

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

После этого вам не нужно ничего делать при изменении отмеченного состояния пунктов меню, иконка должна осознавать себя и реагировать при каждом изменении отмеченного состояния.

person Pawel    schedule 02.09.2019
comment
Для класса DrawableWrapper требуется уровень API 23. - person A. Petrov; 20.10.2020
comment
@A.Petrov A.Petrov Я думаю, вы можете использовать вместо этого androidx.appcompat.graphics.drawable.DrawableWrapper, просто добавьте @SuppressLint("RestrictedApi") поверх класса. - person Pawel; 20.10.2020
comment
@SuppressLint всегда плохой подход. В настоящее время я пришел к решению от @Andrei Tudor Diaconu с основным примечанием: рисуемый список состояний может содержать много состояний (> 2), но затрагиваются только два первых состояния элемента и могут использоваться в сделке с Toolbar.MenuItem - person A. Petrov; 20.10.2020
comment
Я бы поспорил, если это всегда плохой подход. Я предполагаю, что они поместили ограниченную аннотацию поверх этого класса, потому что это всего лишь грубый вспомогательный класс, а не открытый API, но я не вижу проблемы в его игнорировании, поскольку это просто делегат для обернутого объекта (вы можете проверить источник) . - person Pawel; 20.10.2020
comment
›(вы можете проверить источник) - это ключ. Источник на данный момент. Нет никакой гарантии, что этот API выживет при эволюции Jetpack. - person A. Petrov; 20.10.2020
comment
Да, подавление - это риск, но, на мой взгляд, в данном случае его не существует, поскольку этот класс очень прост и не изменялся с момента его создания (кроме миграции androidx). Но если вы не хотите брать его, всегда есть возможность скопировать класс в свой проект, чтобы получить стабильную версию. - person Pawel; 20.10.2020