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.