Android: ListView-Elemente mit mehreren anklickbaren Schaltflächen

182

Ich habe eine, ListViewwo jedes Element in der Liste eine Textansicht und zwei verschiedene Schaltflächen enthält. Etwas wie das:

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

Mit diesem Code kann ich eine OnItemClickListenerfür den gesamten Artikel erstellen :

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

        }

    }
});

Ich möchte jedoch nicht, dass das gesamte Element anklickbar ist, sondern nur die beiden Schaltflächen jedes Listenelements.

Meine Frage ist also, wie ich einen onClickListener für diese beiden Schaltflächen mit den folgenden Parametern implementiere:

  • int button (Auf welche Schaltfläche des Elements wurde geklickt)
  • int position (Dies ist das Element in der Liste, auf das das Klicken auf die Schaltfläche erfolgt ist.)

Update: Ich habe eine Lösung gefunden, wie in meiner Antwort unten beschrieben. Jetzt kann ich über den Touchscreen auf die Schaltfläche klicken / tippen. Ich kann es jedoch nicht manuell mit dem Trackball auswählen. Es wählt immer das gesamte Listenelement aus und geht von dort direkt zum nächsten Listenelement, wobei die Schaltflächen ignoriert werden, obwohl ich .setFocusable(true)und setClickable(true)für die Schaltflächen in festgelegt habe getView().

Ich habe diesen Code auch meinem benutzerdefinierten Listenadapter hinzugefügt:

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

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

Dies führt dazu, dass überhaupt kein Listenelement mehr auswählbar ist. Es hat jedoch nicht geholfen, die verschachtelten Schaltflächen auswählbar zu machen.

Hat jemand eine Idee?

znq
quelle
Werden diese noch benötigt?
Stuart Axon
Wenn Sie sich den BaseAdapter-Code ansehen, werden Sie feststellen, dass areAllItemsEnabled () und isEnabled () nur auf true fest codiert sind, was sie zu einfachen Platzhaltern ohne Logik macht.
Artem Russakovskii
Was ist, wenn ich einen SimpleCursorAdapter verwenden möchte? Muss ich einen benutzerdefinierten Adapter erstellen, der den simpleCursoradapter erweitert?
Oratis

Antworten:

150

Die Lösung dafür ist tatsächlich einfacher als ich dachte. Sie können einfach in der getView()Methode Ihres benutzerdefinierten Adapters einen setOnClickListener () für die von Ihnen verwendeten Schaltflächen hinzufügen .

Alle Daten mit der Taste zugeordnet sind , mit hinzugefügt werden myButton.setTag()in dem getView()und in den OnClickListener zugegriffen werden überview.getTag()

Ich habe eine detaillierte Lösung in meinem Blog als Tutorial veröffentlicht.

znq
quelle
2
Fest. Vielen Dank für die Berichterstattung :-)
znq
Wie genau haben Sie die Datei curItem.url von der Abfrage erhalten? Würden Sie genauer sein?
Oratis
1
Danke znq, das war sehr nützlich für mich ... Hat ein Bündel Zeit gespart.
Nandagopal T
Sehen Sie sich diesen Vortrag um 11:39 an. Es gibt ein hervorragendes Beispiel: youtu.be/wDBM6wVEO70?t=11m39s Dann tun Sie, was @znq sagt ... setTag (), wenn die convertView == null und getTag () im onClick () Methode des onClickListener () der Schaltfläche. Danke dir!
Shehaaz
12
Es wäre großartig, Antworten in SO selbst zu haben und nicht auf eine andere Seite zu verweisen. Der Blog-Link ist tot!
gsb
63

Dies ist eine Art Antwort von anhang @ znq ...

Es gibt viele Fälle, in denen Sie die Zeilenposition für ein angeklicktes Element wissen möchten UND wissen möchten, auf welche Ansicht in der Zeile getippt wurde. Dies wird in Tablet-Benutzeroberflächen viel wichtiger sein.

Sie können dies mit dem folgenden benutzerdefinierten Adapter tun:

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);
        }
    };
}
greg7gkb
quelle
6
oder Sie können auch so etwas verwenden ListView mListView = (ListView) v.getParent (). getParent ();
Max4ever
1
Wie kann ich die Position ermitteln, wenn ich den Adapter in einer separaten Klasse verwende? Ich habe einfach die Position in OnClickListener verwendet, so etwas wie like_c.get (Position). Es wird empfohlen, die Position in der getView-Methode des Adapters endgültig festzulegen. Wenn ich das ändere, ist die Position, die ich bekomme, für alle Elemente in der Ansicht gleich. Könnten Sie mir vorschlagen, wie ich das lösen kann?
Manikandan
Das liegt daran, dass Sie bei Verwendung des Positionsparameters der Methode getView die Position des Elements der Liste erhalten, das zuletzt erstellt wurde, aber nicht das, auf das Sie geklickt haben
Archie.bpgc
@Manikandan: Wenn Sie die Methode getView () des Adapters verwenden, haben Sie die Position bereits in den Methodenparametern an Sie übergeben. In diesem Fall könnten Sie so etwas wie setTag () verwenden, um die Positionsinformationen zu speichern.
Greg7gkb
1
Ich verwende Ihren Code in meiner App, aber das Klickereignis wird nach einer Verzögerung oder wenn ich versuche, durch die Listenansicht zu scrollen, aufgerufen. Irgendwelche Ideen, warum das passiert?
Abdullah Umer
22

Für zukünftige Leser:

Um die Tasten mit dem Trackball manuell auszuwählen, verwenden Sie:

myListView.setItemsCanFocus(true);

Und um den Fokus auf die gesamten Listenelemente zu deaktivieren:

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

Es funktioniert gut für mich, ich kann auf Schaltflächen mit Touchscreen klicken und auch einen Klick über die Tastatur fokussieren

Fabricio PH
quelle
2
Nur dieser hat mir geholfen! Vielen Dank!
Onur
Was ist mit einer ExpandableListView? Ich habe mehrere Ansichten im Element. Ich möchte nur, dass die Ansichten im untergeordneten Element anklickbar sind, danke.
Twlkyao
12

Ich habe nicht viel Erfahrung als die oben genannten Benutzer, aber ich hatte das gleiche Problem und habe es mit der folgenden Lösung gelöst

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

btnRemoveClick Click-Ereignis

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

}
Bhavin Chauhan
quelle
interessante Lösung
Sherpya
9

Wahrscheinlich haben Sie gefunden, wie es geht, aber Sie können anrufen

ListView.setItemsCanFocus(true)

und jetzt werden Ihre Tasten fokussiert

Rafal
quelle
5

Ich bin mir nicht sicher, ob ich der beste Weg sein soll, aber es funktioniert einwandfrei und der gesamte Code bleibt in Ihrem 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;
}

}
mFontolan
quelle
3

Ich weiß, dass es spät ist, aber dies kann helfen. Dies ist ein Beispiel dafür, wie ich eine benutzerdefinierte Adapterklasse für verschiedene Klickaktionen schreibe

 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;
    }
}
Manohar Reddy
quelle
-4

Ist die Plattformlösung für diese Implementierung nicht die Verwendung eines Kontextmenüs, das bei langem Drücken angezeigt wird?

Ist dem Autor der Frage Kontextmenüs bekannt? Das Stapeln von Schaltflächen in einer Listenansicht hat Auswirkungen auf die Leistung, führt zu Unordnung in Ihrer Benutzeroberfläche und verletzt das empfohlene UI-Design für die Plattform.

Auf der Kehrseite; Kontextmenüs - von Natur aus ohne passive Darstellung - sind für den Endbenutzer nicht offensichtlich. Erwägen Sie, das Verhalten zu dokumentieren?

Dieser Leitfaden sollte Ihnen einen guten Start geben.

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

Gusdor
quelle