Android: Elemen ListView dengan beberapa tombol yang dapat diklik

182

Saya memiliki tempat di ListViewmana setiap elemen dalam daftar berisi TextView dan dua Tombol berbeda. Sesuatu seperti ini:

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

Dengan kode ini saya dapat membuat OnItemClickListeneruntuk seluruh item:

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

        }

    }
});

Namun, saya tidak ingin seluruh item dapat diklik, tetapi hanya dua tombol dari setiap elemen daftar.

Jadi pertanyaan saya adalah, bagaimana cara menerapkan onClickListener untuk dua tombol ini dengan parameter berikut:

  • int button (tombol elemen mana yang telah diklik)
  • int position (yang merupakan elemen dalam daftar di mana klik tombol terjadi)

Pembaruan: Saya menemukan solusi seperti yang dijelaskan dalam jawaban saya di bawah ini. Sekarang saya dapat mengklik / mengetuk tombol melalui layar sentuh. Namun, saya tidak dapat secara manual memilihnya dengan trackball. Selalu memilih seluruh item daftar dan dari sana langsung menuju ke item daftar berikutnya mengabaikan tombol, meskipun saya mengatur .setFocusable(true)dan setClickable(true)untuk tombol di getView().

Saya juga menambahkan kode ini ke adaptor daftar kustom saya:

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

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

Hal ini menyebabkan tidak ada item daftar yang dapat dipilih sama sekali. Tapi itu tidak membantu membuat tombol bersarang bisa dipilih.

Adakah yang punya ide?

znq
sumber
Apakah ini masih dibutuhkan?
Stuart Axon
Jika Anda melihat kode BaseAdapter, Anda akan melihat bahwaAllItemsEnabled () dan isEnabled () hanya hardcoded menjadi true, menjadikannya placeholder sederhana tanpa logika.
Artem Russakovskii
Bagaimana jika saya ingin menggunakan SimpleCursorAdapter? Apakah saya harus membuat custom adapter extended simplecursoradapter?
oratis

Jawaban:

150

Solusi untuk ini sebenarnya lebih mudah dari yang saya kira. Anda bisa menambahkan getView()setOnClickListener () pada metode adaptor kustom Anda untuk tombol yang Anda gunakan.

Setiap data yang terkait dengan tombol harus ditambahkan dengan myButton.setTag()dalam getView()dan dapat diakses di onClickListener melaluiview.getTag()

Saya memposting solusi terperinci di blog saya sebagai tutorial.

znq
sumber
2
Tetap. Terima kasih telah melaporkan :-)
znq
seberapa tepatnya Anda mendapatkan curItem.url dari kueri, apakah Anda akan lebih spesifik?
oratis
1
Terima kasih znq, Itu sangat berguna bagi saya ... Menyimpan banyak waktu.
Nandagopal T
Tonton ceramah ini pada pukul 11:39 ada contoh yang sangat baik: youtu.be/wDBM6wVEO70?t=11m39s Lalu lakukan apa yang dikatakan @znq ... setTag () saat convertView == null dan getTag () di onClick () metode onClickListener tombol (). Terima kasih!
Shehaaz
12
alangkah baiknya memiliki jawaban di SO itu sendiri dan tidak menunjuk ke halaman lain. Tautan blog sudah mati!
gsb
63

Ini semacam tambahan @ znq's jawaban ...

Ada banyak kasus di mana Anda ingin mengetahui posisi baris untuk item yang diklik DAN Anda ingin tahu tampilan mana di baris yang diketuk. Ini akan menjadi jauh lebih penting dalam UI tablet.

Anda dapat melakukan ini dengan adaptor khusus berikut:

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
sumber
6
atau Anda juga dapat menggunakan sesuatu seperti ini ListView mListView = (ListView) v.getParent (). getParent ();
maksimal
1
Bagaimana saya bisa mendapatkan posisi jika saya menggunakan adaptor di kelas yang terpisah. Saya cukup menggunakan posisi di dalam OnClickListener, sesuatu seperti like_c.get (posisi) ini. Disarankan untuk membuat posisi ke final dalam metode getView adaptor. Jika saya melakukan perubahan posisi yang saya dapatkan sama untuk semua item di lisview. Bisakah Anda menyarankan saya cara mengatasi ini.
Manikandan
Itu karena ketika Anda menggunakan parameter posisi dari metode getView itu memberi Anda posisi item daftar yang paling baru dibuat tetapi bukan yang Anda klik
Archie.bpgc
@Manikandan: Jika Anda menggunakan metode getView () dari adaptor, maka posisi Anda sudah diteruskan ke Anda di params metode. Dalam hal ini Anda dapat menggunakan sesuatu seperti setTag () untuk menyimpan informasi posisi.
greg7gkb
1
Saya menggunakan kode Anda di aplikasi saya tetapi acara klik dipanggil setelah penundaan atau ketika saya mencoba untuk menggulir tampilan daftar. Adakah ide mengapa ini terjadi?
Abdullah Umer
22

Untuk pembaca masa depan:

Untuk memilih tombol secara manual dengan menggunakan trackball:

myListView.setItemsCanFocus(true);

Dan untuk menonaktifkan fokus pada seluruh daftar item:

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

Ini berfungsi dengan baik untuk saya, saya bisa mengklik tombol dengan layar sentuh dan juga mengubah fokus klik menggunakan tombol

Fabricio PH
sumber
2
Hanya yang ini yang membantu saya! Terima kasih banyak!
Onur
Bagaimana dengan ExpandableListView, saya memiliki beberapa tampilan dalam item, saya hanya ingin tampilan pada anak dapat diklik, terima kasih.
twlkyao
12

Saya tidak memiliki banyak pengalaman daripada pengguna di atas tetapi saya menghadapi masalah yang sama dan saya Memecahkan ini dengan Solusi di bawah ini

<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 Klik acara

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

}
Bhavin Chauhan
sumber
solusi menarik
sherpya
9

Mungkin Anda telah menemukan cara melakukannya, tetapi Anda dapat menelepon

ListView.setItemsCanFocus(true)

dan sekarang tombol Anda akan menangkap fokus

Rafal
sumber
5

Saya tidak yakin tentang menjadi cara terbaik, tetapi berfungsi dengan baik dan semua kode tetap ada di ArrayAdapter Anda.

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
sumber
3

Saya Tahu sudah terlambat tetapi ini mungkin membantu, ini adalah contoh bagaimana saya menulis kelas adaptor khusus untuk berbagai tindakan klik

 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
sumber
-4

Bukankah solusi platform untuk implementasi ini menggunakan menu konteks yang ditampilkan di media lama?

Apakah penulis pertanyaan mengetahui menu konteks? Menumpuk tombol dalam tampilan daftar memiliki implikasi kinerja, akan mengacaukan UI Anda dan melanggar desain UI yang disarankan untuk platform.

Di flipside; menu konteks - pada dasarnya tidak memiliki representasi pasif - tidak jelas bagi pengguna akhir. Pertimbangkan mendokumentasikan perilaku itu?

Panduan ini akan memberi Anda awal yang baik.

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

Gusdor
sumber