Quelle est la différence entre les états sélectionnés, vérifiés et activés sous Android?

Réponses:

182

La différence entre Vérifié et Activé est en fait assez intéressante. Même la documentation Google s'excuse (soulignement ci-dessous ajouté):

... Par exemple, dans une vue de liste avec sélection unique ou multiple activée, les vues du jeu de sélection actuel sont activées. (Euh, oui, nous sommes profondément désolés pour la terminologie ici.) L'état activé est propagé aux enfants de la vue sur laquelle il est défini.

Voici donc la différence:

  1. Activated a été introduit dans Honeycomb, vous ne pouvez donc pas l'utiliser avant cela
  2. Activé est désormais une propriété de chaque vue. Il a les méthodes setActivated () et isActivated ()
  3. Activé se propage aux enfants de la vue sur laquelle il est défini
  4. Checked tourne autour d'une vue implémentant l'interface Checkable. Méthodes setChecked (), isChecked (), toggle ()
  5. ListView (après Honeycomb) appelle setChecked () OU setActivated () selon la version Android comme ci-dessous (extrait du code source 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));
        }
    }

    Notez la variable mCheckStates. Il garde une trace des positions de votre liste qui sont vérifiées / activées. Ceux-ci sont accessibles via, par exemple, getCheckedItemPositions (). Notez également qu'un appel à ListView.setItemChecked () appelle ce qui précède. En d'autres termes, il pourrait également être appelé setItemActivated ().

  6. Avant Honeycomb, nous devions implémenter des solutions de contournement pour refléter state_checked dans nos éléments de liste. Ceci est dû au fait que ListView appelle setChecked () UNIQUEMENT sur la vue la plus haute de la mise en page (et les mises en page n'implémentent pas checkable) ... et il ne se propage PAS sans aide. Ces solutions de contournement étaient de la forme suivante: Étendez la disposition racine pour implémenter Checkable. Dans son constructeur, recherchez récursivement tous les enfants qui implémentent Checkable. Lorsque setChecked () etc ... sont appelés, passez l'appel à ces vues. Si ces vues implémentent des listes d'états dessinables (par exemple une CheckBox) avec un dessin différent pour state_checked alors l'état vérifié est reflété dans l'interface utilisateur.

  7. Pour faire un joli arrière-plan à un élément de liste après Honeycomb, tout ce que vous devez faire est d'avoir une liste d'états dessinable avec un dessinable pour l'état state_activated comme ceci (et utilisez bien sûr setItemChecked ()):

    <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. Pour créer un joli arrière-plan sur un élément de liste avant HoneyComb, vous feriez quelque chose comme ci-dessus pour state_checked et vous devez ÉGALEMENT étendre votre vue supérieure pour implémenter l'interface Checkable. Dans ce contexte, vous devez ensuite indiquer à Android si l'état que vous implémentez est vrai ou faux en implémentant onCreateDrawableState () et en appelant refreshDrawableState () chaque fois que l'état change.

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

... et le code pour implémenter Checkable combiné avec state_checked dans un RelativeLayout pourrait être:

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

Merci à ce qui suit:

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

Stackoverflow: comment ajouter un état de bouton personnalisé

Stackoverflow: vue vérifiable personnalisée qui répond au sélecteur

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
la source
4
cette réponse n'a pas de prix. J'aurais aimé le lire avant d'essayer de savoir comment mettre en œuvre une mise en page Checkable, etc. Merci beaucoup.
Blake Mumford le
12
excellente réponse, mais ne traite pas des éléments "sélectionnés". J'ai trouvé la réponse dans les phrases juste avant celle que vous avez citée: 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.) source
woojoo666
ma couleur d'arrière-plan personnalisée n'apparaît que derrière les éléments sélectionnés / ciblés, non les éléments cochés, lorsque vous utilisez la méthode post-Honeycomb que vous avez publiée ci-dessus: appel setItemChecked()puis utilisation d'un sélecteur avec une propriétéandroid:state_activated="true"
woojoo666
1
Merci beaucoup, j'ai perdu 3 jours à essayer de comprendre cela jusqu'à ce que je décide enfin de me demander "quelle est la différence entre coché, sélectionné et activé", craint que gérer quelque chose d'aussi simple qu'un menu doive être aussi compliqué, sur-ingénierie par les génies de Google, semble presque être un obstacle mis exprès par cette société pour en ralentir les autres.
Gubatron
20

D'après le doc :

  • android: booléen state_selected . " true" si cet élément doit être utilisé lorsque l'objet est la sélection actuelle de l'utilisateur lors de la navigation avec une commande directionnelle (comme lors de la navigation dans une liste avec un pavé directionnel); " false" si cet élément doit être utilisé lorsque l'objet n'est pas sélectionné. L'état sélectionné est utilisé lorsque le focus (android: state_focused) n'est pas suffisant (par exemple lorsque la vue de liste a le focus et qu'un élément qu'il contient est sélectionné avec un d-pad).

  • android: booléen state_checked . " true" si cet élément doit être utilisé lorsque l'objet est vérifié; " false" s'il doit être utilisé lorsque l'objet n'est pas coché.

  • android: state_activated Boolean . " true" si cet élément doit être utilisé lorsque l'objet est activé en tant que sélection persistante (par exemple pour "mettre en surbrillance" l'élément de liste précédemment sélectionné dans une vue de navigation persistante); " false" s'il doit être utilisé lorsque l'objet n'est pas activé. Introduit au niveau d'API 11 .

Je pense que la doc est assez claire, alors quel est le problème?

AMerle
la source
5
Pouvez-vous élaborer sur Android: state_selected. Dans quelles circonstances il est défini sur vrai?
Anderson le
@Anderson cela dépendra du ViewGroup que vous utilisez - ListView, RecyclerView (probablement ses LayoutManagers), GridView peut avoir des implémentations différentes: ListView appelle setFocused là où GridView appelle setSelected par exemple. Il peut s'agir simplement de vérifier votre application sur différentes versions de plate-forme.
ataulm
1
@Anderson: Si vous avez une liste et que l'utilisateur a des touches fléchées, l'une est "sélectionnée", et quand elles fléchent vers le haut / bas, la sélection se déplace vers le haut / vers le bas. Quand ils appuient sur la touche "activer", cela "active" le contrôle, pensez à sa sélection comme vaguement apparentée au survol de la souris, et la vérification / activation comme vaguement semblable à un clic.
Mooing Duck
Je me demandais, je vais utiliser activé pour mettre en surbrillance un élément dans une vue de liste, mais est-ce que cela définit l'activation des autres éléments de la liste sur false ... si ce n'est pas le cas, faites-le pour ne pas avoir à trouver l'autre élément enfant activé et définissez l'activation sur false?
Lion789
0

Voici une autre solution à ce problème: https://github.com/jiahaoliuliu/CustomizedListRow/blob/master/src/com/jiahaoliuliu/android/customizedlistview/MainActivity.java

J'ai remplacé la méthode setOnItemClickListener et vérifié les différents cas dans le code. Mais définitivement la solution de Marvin est bien meilleure.

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