Problema di clic su ListView personalizzato sugli elementi in Android


110

Quindi ho un oggetto ListView personalizzato. Gli elementi dell'elenco hanno due visualizzazioni di testo impilate l'una sull'altra, più una barra di avanzamento orizzontale che voglio che rimanga nascosta finché non faccio effettivamente qualcosa. All'estrema destra c'è una casella di controllo che desidero visualizzare solo quando l'utente deve scaricare gli aggiornamenti nei propri database. Quando disattivo la casella di controllo impostando la visibilità su Visibility.GONE, posso fare clic sugli elementi dell'elenco. Quando la casella di controllo è visibile, non riesco a fare clic su nulla nell'elenco tranne le caselle di controllo. Ho fatto alcune ricerche ma non ho trovato nulla di rilevante per la mia situazione attuale. Ho trovato questa domanda ma sto usando un ArrayAdapter sovrascritto poiché sto usando ArrayLists per contenere l'elenco dei database internamente. Devo solo ottenere la visualizzazione LinearLayout e aggiungere un onClickListener come ha fatto Tom? IO'

Ecco l'XML del layout di riga 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>

Ed ecco la classe che estende ListActivity. Ovviamente è ancora in fase di sviluppo, quindi perdona le cose che mancano o potrebbero essere lasciate in giro:

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
}

modifica: ho ancora questo problema. Sto barando e aggiungendo gestori onClick alle visualizzazioni di testo, ma sembra estremamente stupido che la mia funzione onListItemClick () non venga chiamata affatto quando non faccio clic sulla mia casella di controllo.

Risposte:


243

Il problema è che Android non ti consente di selezionare gli elementi dell'elenco che contengono elementi attivabili. Ho modificato la casella di controllo sulla voce di elenco per avere un attributo in questo modo:

android:focusable="false"

Ora le voci della mia lista che contengono le caselle di controllo (funziona anche per i pulsanti) sono "selezionabili" nel senso tradizionale (si accendono, puoi fare clic in un punto qualsiasi della voce della lista e il gestore "onListItemClick" si attiverà, ecc.).

EDIT: Come aggiornamento, un commentatore ha menzionato "Solo una nota, dopo aver modificato la visibilità del pulsante ho dovuto disabilitare nuovamente lo stato attivo a livello di programmazione".


24
Sembra che questa soluzione non funzioni quando ImageButton viene utilizzato nella voce di elenco. :(
Julian A.

1
Ok, ma cosa succede se voglio che la mia casella di controllo e il mio clic sulla visualizzazione elenco si escludano a vicenda? Se seleziono l'elemento ListView non voglio necessariamente controllare il checkBox. È possibile con questa soluzione?
Mike6679

@ Mike Sì, questa soluzione richiede ancora che la casella di controllo sia selezionata in modo esplicito per poter interagire. Riattiva semplicemente la funzionalità in ListView che misteriosamente viene aggirata quando si hanno elementi attivabili nel layout degli elementi dell'elenco.
MattC

2
Ottima risposta grazie, funziona bene anche con ImageButtons. Solo una nota, dopo aver modificato la visibilità del pulsante ho dovuto disabilitare nuovamente il focus programmaticamente.
Ljdawson

1
potresti anche dover impostare android: clickable = "false"
Mina Samy

43

Se hai ImageButton all'interno dell'elemento dell'elenco, dovresti impostare il descendantFocusabilityvalore su "blocksDescendants" nell'elemento dell'elemento dell'elenco principale.

android:descendantFocusability="blocksDescendants"

E la focusableInTouchModebandiera atrue nella ImageButtonvista.

android:focusableInTouchMode="true"

1
Funziona perfettamente per listitem con pulsanti immagine. Ma questo potrebbe funzionare tranne ImageButton
raksja

12

Ho riscontrato un problema simile e ho scoperto che CheckBox è piuttosto complicato in un ListView. Quello che succede è che impone la sua volontà sull'intero ListItem e in qualche modo sovrascrive onListItemClick. Potresti voler implementare un gestore di clic per questo e impostare anche la proprietà di testo per CheckBox, invece di usare TextViews.

Direi che guarda anche questo oggetto View, potrebbe funzionare meglio del CheckBox

Visualizzazione testo controllato


6
Ho scoperto che se imposti l'impostazione "attivabile" su false per gli elementi attivabili nell'elenco, puoi utilizzare di nuovo la funzione onListItemClick.
MattC

2
Tuttavia, la casella di controllo diventa arancione come l'elemento dell'elenco. Qualcuno sa una soluzione?
erdomester

8

usa questa riga nella vista principale della voce di elenco

Android: descendantFocusability = "blocksDescendants"

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.