Qual é a diferença entre os estados selecionados, verificados e ativados no Android?

Respostas:

182

A diferença entre verificado e ativado é bastante interessante. Até mesmo a documentação do Google é apologética (ênfase adicionada):

... Por exemplo, em uma exibição de lista com seleção única ou múltipla habilitada, as exibições no conjunto de seleção atual são ativadas. (Hum, sim, lamentamos profundamente a terminologia aqui.) O estado ativado é propagado para os filhos da visualização em que está definido.

Então aqui está a diferença:

  1. O Contato foi introduzido no Honeycomb, então você não pode usá-lo antes
  2. Ativado agora é uma propriedade de todas as visualizações. Possui métodos setActivated () e isActivated ()
  3. Ativado se propaga para os filhos da Visualização na qual está definido
  4. Checked gira em torno de uma View que implementa a interface Checkable. Métodos setChecked (), isChecked (), toggle ()
  5. ListView (após Honeycomb) chama setChecked () OU setActivated () dependendo da versão do Android conforme abaixo (obtido do código-fonte do Android):

    if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
        if (child instanceof Checkable) {
            ((Checkable) child).setChecked(mCheckStates.get(position));
        } else if (getContext().getApplicationInfo().targetSdkVersion
                >= android.os.Build.VERSION_CODES.HONEYCOMB) {
            child.setActivated(mCheckStates.get(position));
        }
    }

    Observe a variável mCheckStates. Ele mantém o controle de quais posições em sua lista são verificadas / ativadas. Eles são acessíveis via, por exemplo, getCheckedItemPositions (). Observe também que uma chamada para ListView.setItemChecked () invoca o acima. Em outras palavras, ele também poderia ser chamado de setItemActivated ().

  6. Antes do Honeycomb, tínhamos que implementar soluções alternativas para refletir state_checked em nossos itens de lista. Isso ocorre porque ListView chama setChecked () SOMENTE na View superior no layout (e os layouts não implementam verificável) ... e NÃO se propaga sem ajuda. Essas soluções alternativas tinham o seguinte formato: Estenda o layout raiz para implementar Checkable. Em seu construtor, encontre recursivamente todos os filhos que implementam Checkable. Quando setChecked () etc ... são chamados, passe a chamada para essas visualizações. Se essas visualizações implementam drawables da lista de estados (por exemplo, um CheckBox) com um drawable diferente para state_checked, então o estado verificado é refletido na IU.

  7. Para fazer um bom plano de fundo para um item de lista após o Honeycomb, tudo que você precisa fazer é ter um drawable da lista de estados com um drawable para o estado state_activated assim (e usar setItemChecked () é claro):

    <item android:state_pressed="true"
        android:drawable="@drawable/list_item_bg_pressed"/>
    <item android:state_activated="true"
        android:drawable="@drawable/list_item_bg_activated"/>
    <item android:drawable="@drawable/list_item_bg_normal"/>

  8. Para fazer um bom pano de fundo para um item de lista antes do HoneyComb, você faria algo como o acima para state_checked e TAMBÉM precisa estender sua visão superior para implementar a interface Checkable. Dentro disso, você precisa dizer ao Android se o estado que está implementando é verdadeiro ou falso, implementando onCreateDrawableState () e chamando refreshDrawableState () sempre que o estado muda.

    <item android:state_pressed="true"
        android:drawable="@drawable/list_item_bg_pressed"/>
    <item android:state_checked="true"
        android:drawable="@drawable/list_item_bg_checked"/>
    <item android:drawable="@drawable/list_item_bg_normal"/>

... e o código para implementar Checkable combinado com state_checked em um RelativeLayout poderia ser:

public class RelativeLayoutCheckable extends RelativeLayout implements Checkable {

    public RelativeLayoutCheckable(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RelativeLayoutCheckable(Context context) {
        super(context);
    }

    private boolean mChecked = false;

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
    }
    @Override
    public boolean isChecked() {
        return mChecked;
    }

    @Override
    public void setChecked(boolean checked) {
        mChecked = checked;
        refreshDrawableState();
    }

    private static final int[] mCheckedStateSet = {
        android.R.attr.state_checked,
    };

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            mergeDrawableStates(drawableState, mCheckedStateSet);
        }
        return drawableState;
    }    

    @Override
    public void toggle() {
        setChecked(!mChecked);
    }
}

Graças ao seguinte:

http://sriramramani.wordpress.com/2012/11/17/custom-states/

Stackoverflow: como adicionar um estado de botão personalizado

Stackoverflow: Visualização verificável personalizada que responde ao Seletor

http://www.charlesharley.com/2012/programming/custom-drawable-states-in-android/

http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList

http://blog.marvinlabs.com/2010/10/29/custom-listview-ability-check-items/

Martin Harvey
fonte
4
esta resposta não tem preço. Gostaria de ler antes de tentar descobrir como implementar um layout Checkable etc. Muito obrigado.
Blake Mumford
12
ótima resposta, mas não aborda os itens "selecionados". Encontrei a resposta nas frases ANTES da frase que você citou: Selection is a transient property, representing the view (hierarchy) the user is currently interacting with. Activation is a longer-term state that the user can move views in and out of. For example, in a list view with single or multiple selection enabled, the views in the current selection set are activated. (Um, yeah, we are deeply sorry about the terminology here.) fonte
woojoo666
minha cor de fundo personalizada só aparece atrás dos itens selecionados / focados, não dos itens marcados, ao usar o método pós-Honeycomb que você postou acima: chamando setItemChecked()e usando um seletor com propriedadeandroid:state_activated="true"
woojoo666
1
Muito obrigado, eu perdi 3 dias tentando descobrir isso até que finalmente decidi me perguntar "qual é a diferença entre verificado, selecionado e ativado", é uma merda que lidar com algo tão simples como um menu tem que ser tão complicado, superengenharia pelos gênios do Google, quase parece um obstáculo colocado propositalmente por esta empresa para atrasar os outros.
Gubatron
20

De acordo com o doc :

  • android: booleano selecionado por estado . " true" se este item deve ser usado quando o objeto é a seleção atual do usuário ao navegar com um controle direcional (como ao navegar por uma lista com um d-pad); " false" se este item deve ser usado quando o objeto não está selecionado. O estado selecionado é usado quando o foco (android: state_focused) não é suficiente (como quando a exibição de lista tem foco e um item dentro dela é selecionado com um d-pad).

  • android: booleano state_checked . " true" se este item deve ser usado quando o objeto é verificado; " false" se deve ser usado quando o objeto está desmarcado.

  • android: booleano state_activated . " true" se este item deve ser usado quando o objeto é ativado como a seleção persistente (como para "destacar" o item da lista selecionado anteriormente em uma visualização de navegação persistente); " false" se deve ser usado quando o objeto não está ativado. Introduzido na API de nível 11 .

Acho que o médico é bastante claro, então qual é o problema?

AMerle
fonte
5
Você pode elaborar sobre android: state_selected. Quais são as circunstâncias em que é definido como verdadeiro?
Anderson,
@Anderson vai depender do ViewGroup que você está usando - ListView, RecyclerView (provavelmente seus LayoutManagers), GridView pode ter implementações diferentes: ListView chama setFocused onde GridView chama setSelected por exemplo. Pode ser apenas o caso de verificar seu aplicativo em diferentes versões de plataforma.
ataulm
1
@Anderson: Se você tem uma lista, e o usuário tem teclas de seta, uma está "selecionada", e quando elas vão para cima / para baixo, a seleção move para cima / baixo. Quando pressionam a tecla "ativar", ela "ativa" o controle, pense na seleção como vagamente semelhante ao mouseover e verificação / ativação como vagamente semelhante a clicar.
Mooing Duck de
Eu estava me perguntando, vou usar o ativado para destacar um item em uma exibição de lista, mas ele define a ativação para outros itens da lista como falso ... se não, faça qualquer um desses, então não tenho que encontrar o outro item filho ativado e definir a ativação como falsa?
Lion789 de
0

Aqui está outra solução para este problema: https://github.com/jiahaoliuliu/CustomizedListRow/blob/master/src/com/jiahaoliuliu/android/customizedlistview/MainActivity.java

Substituí o método setOnItemClickListener e verifiquei casos diferentes no código. Mas definitivamente a solução do Marvin é muito melhor.

listView.setOnItemClickListener(new OnItemClickListener() {

@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position,
        long id) {
    CheckedTextView checkedTextView =
            (CheckedTextView)view.findViewById(R.id.checkedTextView);
    // Save the actual selected row data
    boolean checked = checkedTextView.isChecked();
    int choiceMode = listView.getChoiceMode();
    switch (choiceMode) {
    // Not choosing anything
    case (ListView.CHOICE_MODE_NONE):
        // Clear all selected data
        clearSelection();
        //printCheckedElements();
        break;
    // Single choice
    case (ListView.CHOICE_MODE_SINGLE):
        // Clear all the selected data
        // Revert the actual row data
        clearSelection();
        toggle(checked, checkedTextView, position);
        //printCheckedElements();
        break;
    // Multiple choice
    case (ListView.CHOICE_MODE_MULTIPLE):
    case (ListView.CHOICE_MODE_MULTIPLE_MODAL):
        // Revert the actual selected row data
        toggle(checked, checkedTextView, position);
        //printCheckedElements();
        break;
    }
    }
});
Jiahao
fonte