¿Cuál es la diferencia entre los estados seleccionados, marcados y activados en Android?

Respuestas:

182

La diferencia entre Checked y Activated es bastante interesante. Incluso la documentación de Google se disculpa (el énfasis se agrega a continuación):

... Por ejemplo, en una vista de lista con selección única o múltiple habilitada, se activan las vistas en el conjunto de selección actual. (Um, sí, lamentamos profundamente la terminología aquí.) El estado activado se propaga a los hijos de la vista en la que está establecido.

Entonces aquí está la diferencia:

  1. Activated se introdujo en Honeycomb, por lo que no puede usarlo antes de eso
  2. Activado es ahora una propiedad de todas las vistas. Tiene los métodos setActivated () e isActivated ()
  3. Se propaga activado a los elementos secundarios de la Vista en la que está configurado
  4. Checked gira en torno a una vista que implementa la interfaz Checkable. Métodos setChecked (), isChecked (), toggle ()
  5. ListView (después de Honeycomb) llama a setChecked () O setActivated () dependiendo de la versión de Android como se muestra a continuación (tomado del código fuente de 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));
        }
    }

    Tenga en cuenta la variable mCheckStates. Realiza un seguimiento de las posiciones en su lista que están marcadas / activadas. Se puede acceder a ellos mediante, por ejemplo, getCheckedItemPositions (). Tenga en cuenta también que una llamada a ListView.setItemChecked () invoca lo anterior. En otras palabras, también podría llamarse setItemActivated ().

  6. Antes de Honeycomb, tuvimos que implementar soluciones para reflejar state_checked en los elementos de nuestra lista. Esto se debe a que ListView llama a setChecked () SÓLO en la Vista superior del diseño (y los diseños no se implementan como comprobables) ... y NO se propaga sin ayuda. Estas soluciones alternativas fueron de la siguiente forma: Extienda el diseño raíz para implementar Checkable. En su constructor, busque de forma recursiva todos los elementos secundarios que implementen Checkable. Cuando se llama a setChecked () etc ..., pase la llamada a esas Vistas. Si esas vistas implementan elementos de diseño de lista de estado (por ejemplo, un CheckBox) con un elemento de diseño diferente para state_checked, el estado marcado se refleja en la interfaz de usuario.

  7. Para hacer un fondo agradable para un elemento de la lista después de Honeycomb, todo lo que necesita hacer es tener una lista de estados dibujable con un elemento de dibujo para el estado state_activated como este (y usar setItemChecked () por supuesto):

    <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 hacer un fondo agradable para un elemento de la lista antes de HoneyComb, haría algo como lo anterior para state_checked y TAMBIÉN necesita extender su vista superior para implementar la interfaz Checkable. Dentro de eso, debe decirle a Android si el estado que está implementando es verdadero o falso al implementar onCreateDrawableState () y llamar a refreshDrawableState () siempre que el estado cambie.

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

... y el código para implementar Checkable combinado con state_checked en un RelativeLayout podría 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);
    }
}

Gracias a lo siguiente:

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

Stackoverflow: Cómo agregar un estado de botón personalizado

Stackoverflow: Vista comprobable personalizada que responde al selector

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
fuente
4
esta respuesta no tiene precio. Ojalá lo hubiera leído antes de intentar averiguar cómo implementar un diseño Checkable, etc. Muchas gracias.
Blake Mumford
12
gran respuesta, pero no aborda elementos "seleccionados". Encontré la respuesta en las oraciones JUSTO ANTES de la que citó: 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.) fuente
woojoo666
mi color de fondo personalizado solo aparece detrás de los elementos seleccionados / enfocados, no de los elementos marcados, cuando se usa el método post-Honeycomb que publicó anteriormente: llamar setItemChecked()y luego usar un selector con propiedadandroid:state_activated="true"
woojoo666
1
Muchas gracias, perdí 3 días tratando de resolver esto hasta que finalmente decidí preguntarme "¿cuál es la diferencia entre marcado, seleccionado y activado?", Apesta que lidiar con algo tan simple como un menú tiene que ser así de complicado, sobreingeniería por los genios de Google, casi parece un obstáculo puesto a propósito por esta empresa para frenar a otras.
Gubatron
20

Según el doc :

  • android: state_selected booleano . " true" si este elemento debe usarse cuando el objeto es la selección actual del usuario cuando se navega con un control direccional (como cuando se navega a través de una lista con un d-pad); " false" si este elemento se debe utilizar cuando el objeto no está seleccionado. El estado seleccionado se usa cuando el enfoque (android: state_focused) no es suficiente (como cuando la vista de lista tiene el enfoque y un elemento dentro de él se selecciona con un d-pad).

  • android: state_checked booleano . " true" si este elemento debe usarse cuando el objeto está marcado; " false" si debe usarse cuando el objeto no está marcado.

  • android: state_activated booleano . " true" si este elemento debe usarse cuando el objeto está activado como la selección persistente (como para "resaltar" el elemento de la lista previamente seleccionado en una vista de navegación persistente); " false" si se debe utilizar cuando el objeto no está activado. Introducido en API nivel 11 .

Creo que el documento es bastante claro, entonces, ¿cuál es el problema?

AMerle
fuente
5
¿Puedes dar más detalles sobre android: state_selected. ¿Cuáles son las circunstancias en las que se establece como verdadero?
Anderson
@Anderson dependerá del ViewGroup que esté utilizando: ListView, RecyclerView (probablemente sus LayoutManagers), GridView puede tener diferentes implementaciones: ListView llama a setFocused donde GridView llama a setSelected, por ejemplo. Puede ser solo un caso de verificar su aplicación en diferentes versiones de plataforma.
ataulm
1
@Anderson: Si tiene una lista y el usuario tiene teclas de flecha, una está "seleccionada", y cuando apuntan hacia arriba / abajo, la selección se mueve hacia arriba / abajo. Cuando presionan la tecla "activar", "activa" el control, piense en la selección como algo vagamente parecido a pasar el ratón por encima, y ​​la verificación / activación como algo parecido a hacer clic.
Pato mudo
Me preguntaba, voy a usar activado para resaltar un elemento en una vista de lista, pero ¿establece la activación para otros elementos de la lista en falso ... si no hago alguno de estos, haga eso para no tener que buscar el otro elemento secundario activado y establece la activación en falso?
Lion789
0

Aquí hay otra solución para este problema: https://github.com/jiahaoliuliu/CustomizedListRow/blob/master/src/com/jiahaoliuliu/android/customizedlistview/MainActivity.java

He anulado el método setOnItemClickListener y verifico diferentes casos en el código. Pero definitivamente la solución de Marvin es mucho mejor.

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
fuente