Problème de clic ListView personnalisé sur les éléments dans Android

110

J'ai donc un objet ListView personnalisé. Les éléments de la liste ont deux vues de texte empilées l'une sur l'autre, plus une barre de progression horizontale que je veux rester cachée jusqu'à ce que je fasse réellement quelque chose. À l'extrême droite, il y a une case à cocher que je souhaite afficher uniquement lorsque l'utilisateur a besoin de télécharger des mises à jour dans sa ou ses bases de données. Lorsque je désactive la case à cocher en définissant la visibilité sur Visibility.GONE, je peux cliquer sur les éléments de la liste. Lorsque la case à cocher est visible, je ne peux pas cliquer sur quoi que ce soit dans la liste, à l'exception des cases à cocher. J'ai fait quelques recherches mais je n'ai rien trouvé de pertinent à ma situation actuelle. J'ai trouvé cette questionmais j'utilise un ArrayAdapter remplacé car j'utilise ArrayLists pour contenir la liste des bases de données en interne. Dois-je simplement obtenir la vue LinearLayout et ajouter un onClickListener comme Tom l'a fait? Je ne suis pas sûr.

Voici le XML de disposition de ligne de listview:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:padding="6dip">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dip"
        android:layout_weight="1"
        android:layout_height="fill_parent">
        <TextView
            android:id="@+id/UpdateNameText"
            android:layout_width="wrap_content"
            android:layout_height="0dip"
            android:layout_weight="1"
            android:textSize="18sp"
            android:gravity="center_vertical"
            />
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="0dip"
            android:layout_weight="1"
            android:id="@+id/UpdateStatusText"
            android:singleLine="true"
            android:ellipsize="marquee"
            />
        <ProgressBar android:id="@+id/UpdateProgress" 
                     android:layout_width="fill_parent" 
                     android:layout_height="wrap_content"
                     android:indeterminateOnly="false" 
                     android:progressDrawable="@android:drawable/progress_horizontal" 
                     android:indeterminateDrawable="@android:drawable/progress_indeterminate_horizontal" 
                     android:minHeight="10dip" 
                     android:maxHeight="10dip"                    
                     />
    </LinearLayout>
    <CheckBox android:text="" 
              android:id="@+id/UpdateCheckBox" 
              android:layout_width="wrap_content" 
              android:layout_height="wrap_content" 
              />
</LinearLayout>

Et voici la classe qui étend ListActivity. De toute évidence, il est encore en développement, alors pardonnez les choses qui manquent ou qui pourraient être laissées en suspens:

public class UpdateActivity extends ListActivity {

    AccountManager lookupDb;
    boolean allSelected;
    UpdateListAdapter list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        lookupDb = new AccountManager(this);
        lookupDb.loadUpdates();

        setContentView(R.layout.update);
        allSelected = false;

        list = new UpdateListAdapter(this, R.layout.update_row, lookupDb.getUpdateItems());
        setListAdapter(list);

        Button btnEnterRegCode = (Button)findViewById(R.id.btnUpdateRegister);
        btnEnterRegCode.setVisibility(View.GONE);

        Button btnSelectAll = (Button)findViewById(R.id.btnSelectAll);
        btnSelectAll.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                allSelected = !allSelected;

                for(int i=0; i < lookupDb.getUpdateItems().size(); i++) {
                    lookupDb.getUpdateItem(i).setSelected(!lookupDb.getUpdateItem(i).isSelected());
                }

                list.notifyDataSetChanged();
                // loop through each UpdateItem and set the selected attribute to the inverse 

            } // end onClick
        }); // end setOnClickListener

        Button btnUpdate = (Button)findViewById(R.id.btnUpdate);
        btnUpdate.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
            } // end onClick
        }); // end setOnClickListener

        lookupDb.close();
    } // end onCreate


    @Override
    protected void onDestroy() {
        super.onDestroy();

        for (UpdateItem item : lookupDb.getUpdateItems()) {
            item.getDatabase().close();        
        }
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        UpdateItem item = lookupDb.getUpdateItem(position);

        if (item != null) {
            item.setSelected(!item.isSelected());
            list.notifyDataSetChanged();
        }
    }

    private class UpdateListAdapter extends ArrayAdapter<UpdateItem> {
        private List<UpdateItem> items;

        public UpdateListAdapter(Context context, int textViewResourceId, List<UpdateItem> items) {
            super(context, textViewResourceId, items);
            this.items = items;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View row = null;

            if (convertView == null) {
                LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                row = li.inflate(R.layout.update_row, null);
            } else {
                row = convertView;
            }

            UpdateItem item = items.get(position);

            if (item != null) {
                TextView upper = (TextView)row.findViewById(R.id.UpdateNameText);
                TextView lower = (TextView)row.findViewById(R.id.UpdateStatusText);
                CheckBox cb = (CheckBox)row.findViewById(R.id.UpdateCheckBox);

                upper.setText(item.getName());
                lower.setText(item.getStatusText());

                if (item.getStatusCode() == UpdateItem.UP_TO_DATE) {
                    cb.setVisibility(View.GONE);
                } else {
                    cb.setVisibility(View.VISIBLE);
                    cb.setChecked(item.isSelected());
                }

                ProgressBar pb = (ProgressBar)row.findViewById(R.id.UpdateProgress);
                pb.setVisibility(View.GONE);
            }
            return row;
        }

    } // end inner class UpdateListAdapter
}

edit: J'ai toujours ce problème. Je triche et j'ajoute des gestionnaires onClick aux textviews, mais il semble extrêmement stupide que ma fonction onListItemClick () ne soit pas appelée du tout lorsque je ne clique pas sur ma case à cocher.

MattC
la source

Réponses:

243

Le problème est qu'Android ne vous permet pas de sélectionner des éléments de liste contenant des éléments focalisables. J'ai modifié la case à cocher sur l'élément de liste pour avoir un attribut comme ceci:

android:focusable="false"

Maintenant, mes éléments de liste qui contiennent des cases à cocher (fonctionne aussi pour les boutons) sont "sélectionnables" au sens traditionnel (ils s'allument, vous pouvez cliquer n'importe où dans l'élément de liste et le gestionnaire "onListItemClick" se déclenchera, etc.).

EDIT: En guise de mise à jour, un commentateur a mentionné "Juste une note, après avoir changé la visibilité du bouton, j'ai dû désactiver à nouveau le focus par programme."

MattC
la source
24
Apparaît que cette solution ne fonctionne pas lorsque ImageButton est utilisé dans l'élément de liste. :(
Julian A.
1
Ok, mais que faire si je veux que ma case à cocher et ma vue de liste cliquent pour s'exclure mutuellement? Si je sélectionne l'élément ListView, je ne veux pas nécessairement vérifier la case à cocher. Est-ce possible avec cette solution?
Mike6679
@Mike Oui, cette solution nécessite toujours de cliquer explicitement sur la case pour pouvoir interagir avec. Il réactive simplement les fonctionnalités de ListView qui sont mystérieusement contournées lorsque vous avez des éléments focalisables dans la disposition de votre élément de liste.
MattC
2
Excellente réponse merci, fonctionne bien avec ImageButtons aussi. Juste une note, après avoir changé la visibilité du bouton, j'ai dû désactiver à nouveau le focus par programme.
Ljdawson
1
vous devrez peut-être également définir android: clickable = "false"
Mina Samy
43

Si vous avez ImageButton à l'intérieur de l'élément de liste, vous devez définir la descendantFocusabilityvaleur sur 'blocksDescendants' dans l'élément d'élément de liste racine.

android:descendantFocusability="blocksDescendants"

Et le focusableInTouchModedrapeau truedans la ImageButtonvue.

android:focusableInTouchMode="true"
micnoy
la source
1
Cela fonctionne parfaitement pour les éléments de liste ayant des boutons d'image. Mais cela pourrait fonctionner sauf ImageButton
raksja
12

J'ai eu un problème similaire et j'ai constaté que la CheckBox est plutôt capricieuse dans un ListView. Ce qui se passe, c'est qu'il impose sa volonté à l'ensemble de ListItem et remplace en quelque sorte le onListItemClick. Vous souhaiterez peut-être implémenter un gestionnaire de clics pour cela et définir également la propriété de texte pour le CheckBox, au lieu d'utiliser les TextViews.

Je dirais que regardez également cet objet View, cela peut fonctionner mieux que le CheckBox

Affichage du texte vérifié

Andrew Burgess
la source
6
J'ai trouvé que si vous définissez le paramètre «focusable» sur false pour les éléments focusables de la liste, vous pouvez à nouveau utiliser votre fonction onListItemClick.
MattC
2
Cependant, la case à cocher devient orange comme l'élément de liste. Quelqu'un connaît-il une solution?
erdomester
8

utiliser cette ligne dans la vue racine de l'élément de liste

android: descendantFocusability = "blocksDescendants"

Suresh Kumar
la source