Android: elementi ListView con più pulsanti selezionabili


182

Ho un ListViewdove ogni elemento nella lista contiene un TextView e due diversi pulsanti. Qualcosa come questo:

ListView
--------------------
[Text]
[Button 1][Button 2]
--------------------
[Text]
[Button 1][Button 2]
--------------------
... (and so on) ...

Con questo codice posso creare un OnItemClickListenerper l'intero articolo:

listView.setOnItemClickListener(new OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> list, View view, int position, long id) {
        Log.i(TAG, "onListItemClick: " + position);

        }

    }
});

Tuttavia, non voglio che l'intero elemento sia selezionabile, ma solo i due pulsanti di ciascun elemento dell'elenco.

Quindi la mia domanda è: come posso implementare un onClickListener per questi due pulsanti con i seguenti parametri:

  • int button (quale pulsante dell'elemento è stato cliccato)
  • int position (che è l'elemento nell'elenco in cui si è verificato il clic del pulsante)

Aggiornamento: ho trovato una soluzione come descritto nella mia risposta di seguito. Ora posso fare clic / toccare il pulsante tramite il touchscreen. Tuttavia, non riesco a selezionarlo manualmente con la trackball. Seleziona sempre l'intera voce dell'elenco e da lì passa direttamente alla voce di elenco successiva ignorando i pulsanti, anche se ho impostato .setFocusable(true)e setClickable(true)per i pulsanti getView().

Ho anche aggiunto questo codice al mio adattatore elenco personalizzato:

@Override
public boolean  areAllItemsEnabled() {
    return false;           
}

@Override
public boolean isEnabled(int position) {
        return false;
}

Questo fa sì che nessun elemento dell'elenco sia più selezionabile. Ma non ha aiutato a rendere selezionabili i pulsanti nidificati.

Qualcuno ha un'idea?


Sono ancora necessari?
— Stuart Axon,

Se guardi il codice BaseAdapter, vedresti che areAllItemsEnabled () e isEnabled () sono semplicemente codificati su true, rendendoli semplici segnaposto senza alcuna logica.
— Artem Russakovskii,

Cosa succede se desidero utilizzare un SimpleCursorAdapter? Devo creare un adattatore personalizzato che estenda il semplicecursoradapter?
— oratis,

Risposte:


150

La soluzione a questo è in realtà più semplice di quanto pensassi. Puoi semplicemente aggiungere nel getView()metodo dell'adattatore personalizzato un setOnClickListener () per i pulsanti che stai utilizzando.

Tutti i dati associati al pulsante devono essere aggiunti con myButton.setTag()in getView()e sono accessibili in onClickListener tramiteview.getTag()

Ho pubblicato una soluzione dettagliata sul mio blog come tutorial.


2
Fisso. Grazie per la segnalazione :-)
— znq,

quanto esattamente hai ottenuto il curItem.url dalla query, saresti più specifico?
— oratis,

1
Grazie znq, è stato molto utile per me ... Ho risparmiato un sacco di tempo.
— Nandagopal T

Guarda questo discorso alle 11:39 c'è un eccellente esempio: youtu.be/wDBM6wVEO70?t=11m39s Quindi fai quello che dice @znq ... setTag () quando convertView == null e getTag () in onClick () metodo del pulsante onClickListener (). Grazie!
— Shehaaz,

12
sarebbe bello avere risposte in SO stesso e non puntare a qualche altra pagina. Il link al blog è morto!
— gsb

63

Questa è una specie di risposta di un'appendice @ znq ...

Ci sono molti casi in cui vuoi conoscere la posizione della riga per un elemento cliccato E vuoi sapere quale vista nella riga è stata toccata. Questo sarà molto più importante nelle UI dei tablet.

Puoi farlo con il seguente adattatore personalizzato:

private static class CustomCursorAdapter extends CursorAdapter {

    protected ListView mListView;

    protected static class RowViewHolder {
        public TextView mTitle;
        public TextView mText;
    }

    public CustomCursorAdapter(Activity activity) {
        super();
        mListView = activity.getListView();
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        // do what you need to do
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View view = View.inflate(context, R.layout.row_layout, null);

        RowViewHolder holder = new RowViewHolder();
        holder.mTitle = (TextView) view.findViewById(R.id.Title);
        holder.mText = (TextView) view.findViewById(R.id.Text);

        holder.mTitle.setOnClickListener(mOnTitleClickListener);
        holder.mText.setOnClickListener(mOnTextClickListener);

        view.setTag(holder);

        return view;
    }

    private OnClickListener mOnTitleClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            final int position = mListView.getPositionForView((View) v.getParent());
            Log.v(TAG, "Title clicked, row %d", position);
        }
    };

    private OnClickListener mOnTextClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            final int position = mListView.getPositionForView((View) v.getParent());
            Log.v(TAG, "Text clicked, row %d", position);
        }
    };
}

6
oppure puoi anche usare qualcosa di simile a ListView mListView = (ListView) v.getParent (). getParent ();
— max4ever

1
Come posso ottenere la posizione se uso l'adattatore in una classe separata. Ho semplicemente usato la posizione all'interno di OnClickListener, qualcosa del genere like_c.get (position). Suggerisce di rendere definitiva la posizione nel metodo getView dell'adattatore. Se faccio quel cambiamento, la posizione che ottengo è la stessa per tutti gli oggetti nella vista. Potresti suggerirmi come risolverlo.
— Manikandan,

Questo perché quando usi il parametro position del metodo getView ti dà la posizione dell'elemento della lista che è stato creato più di recente, ma non quello su cui hai fatto clic
— Archie.bpgc

@Manikandan: se stai usando il metodo getView () dell'adattatore, hai già la posizione passata nei parametri del metodo. In tal caso, è possibile utilizzare qualcosa come setTag () per memorizzare le informazioni sulla posizione.
— greg7gkb,

1
Sto usando il tuo codice nella mia app ma l'evento click viene chiamato dopo un ritardo o quando provo a scorrere la visualizzazione elenco. Qualche idea sul perché questo stia accadendo?
— Abdullah Umer,

22

Per i futuri lettori:

Per selezionare manualmente i pulsanti con la trackball usare:

myListView.setItemsCanFocus(true);

E per disabilitare lo stato attivo su tutti gli elementi dell'elenco:

myListView.setFocusable(false);
myListView.setFocusableInTouchMode(false);
myListView.setClickable(false);

Funziona bene per me, posso fare clic sui pulsanti con touchscreen e anche mettere a fuoco un clic usando la tastiera


2
Solo questo mi ha aiutato! Grazie mille!
— Onur,

Che dire di un ExpandableListView, ho più viste nell'elemento, voglio solo le viste nel bambino cliccabili, grazie.
— twlkyao,

12

Non ho molta esperienza rispetto agli utenti precedenti ma ho affrontato lo stesso problema e ho risolto questo problema con la soluzione di seguito

<Button
        android:id="@+id/btnRemove"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/btnEdit"
        android:layout_weight="1"
        android:background="@drawable/btn"
        android:text="@string/remove" 
        android:onClick="btnRemoveClick"
        />

Evento Click btnRemoveClick

public void btnRemoveClick(View v)
{
    final int position = listviewItem.getPositionForView((View) v.getParent()); 
    listItem.remove(position);
    ItemAdapter.notifyDataSetChanged();

}

soluzione interessante
— Sherpya,

9

Probabilmente hai trovato il modo di farlo, ma puoi chiamare

ListView.setItemsCanFocus(true)

e ora i tuoi pulsanti si accenderanno


5

Non sono sicuro di essere il modo migliore, ma funziona bene e tutto il codice rimane nel tuo ArrayAdapter.

package br.com.fontolan.pessoas.arrayadapter;

import java.util.List;

import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import br.com.fontolan.pessoas.R;
import br.com.fontolan.pessoas.model.Telefone;

public class TelefoneArrayAdapter extends ArrayAdapter<Telefone> {

private TelefoneArrayAdapter telefoneArrayAdapter = null;
private Context context;
private EditText tipoEditText = null;
private EditText telefoneEditText = null;
private ImageView deleteImageView = null;

public TelefoneArrayAdapter(Context context, List<Telefone> values) {
    super(context, R.layout.telefone_form, values);
    this.telefoneArrayAdapter = this;
    this.context = context;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View view = inflater.inflate(R.layout.telefone_form, parent, false);

    tipoEditText = (EditText) view.findViewById(R.id.telefone_form_tipo);
    telefoneEditText = (EditText) view.findViewById(R.id.telefone_form_telefone);
    deleteImageView = (ImageView) view.findViewById(R.id.telefone_form_delete_image);

    final int i = position;
    final Telefone telefone = this.getItem(position);
    tipoEditText.setText(telefone.getTipo());
    telefoneEditText.setText(telefone.getTelefone());

    TextWatcher tipoTextWatcher = new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void afterTextChanged(Editable s) {
            telefoneArrayAdapter.getItem(i).setTipo(s.toString());
            telefoneArrayAdapter.getItem(i).setIsDirty(true);
        }
    };

    TextWatcher telefoneTextWatcher = new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void afterTextChanged(Editable s) {
            telefoneArrayAdapter.getItem(i).setTelefone(s.toString());
            telefoneArrayAdapter.getItem(i).setIsDirty(true);
        }
    };

    tipoEditText.addTextChangedListener(tipoTextWatcher);
    telefoneEditText.addTextChangedListener(telefoneTextWatcher);

    deleteImageView.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            telefoneArrayAdapter.remove(telefone);
        }
    });

    return view;
}

}

3

So che è tardi ma questo può essere d'aiuto, questo è un esempio di come scrivo la classe dell'adattatore personalizzata per diverse azioni di clic

 public class CustomAdapter extends BaseAdapter {

    TextView title;
  Button button1,button2;

    public long getItemId(int position) {
        return position;
    }

    public int getCount() {
        return mAlBasicItemsnav.size();  // size of your list array
    }

    public Object getItem(int position) {
        return position;
    }

    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = getLayoutInflater().inflate(R.layout.listnavsub_layout, null, false); // use sublayout which you want to inflate in your each list item
        }

        title = (TextView) convertView.findViewById(R.id.textViewnav); // see you have to find id by using convertView.findViewById 
        title.setText(mAlBasicItemsnav.get(position));
      button1=(Button) convertView.findViewById(R.id.button1);
      button1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //your click action 

           // if you have different click action at different positions then
            if(position==0)
              {
                       //click action of 1st list item on button click
        }
           if(position==1)
              {
                       //click action of 2st list item on button click
        }
    });

 // similarly for button 2

   button2=(Button) convertView.findViewById(R.id.button2);
      button2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //your click action 

    });



        return convertView;
    }
}

-4

La soluzione di piattaforma per questa implementazione non utilizza un menu di scelta rapida che viene visualizzato a lungo?

L'autore della domanda è a conoscenza dei menu di scelta rapida? L'impilamento dei pulsanti in una visualizzazione elenco ha implicazioni sulle prestazioni, ingombrerà la tua interfaccia utente e violerà il design dell'interfaccia utente consigliato per la piattaforma.

Il rovescio della medaglia; i menu contestuali - per loro natura non avere una rappresentazione passiva - non sono ovvi per l'utente finale. Considerare di documentare il comportamento?

Questa guida dovrebbe darti un buon inizio.

http://www.mikeplate.com/2010/01/21/show-a-context-menu-for-long-clicks-in-an-android-listview/

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.