Bagaimana cara menyorot item yang dipilih dengan benar di RecyclerView?

146

Saya mencoba menggunakan RecyclerViewsebagai horizontal ListView. Saya mencoba mencari cara untuk menyorot item yang dipilih. Ketika saya mengklik salah satu item, itu akan dipilih dan disorot dengan benar tetapi ketika saya mengklik yang lain, yang kedua akan disorot dengan yang lebih tua.

Inilah fungsi onClick saya:

@Override
public void onClick(View view) {

    if(selectedListItem!=null){
        Log.d(TAG, "selectedListItem " + getPosition() + " " + item);
        selectedListItem.setBackgroundColor(Color.RED);
    }
    Log.d(TAG, "onClick " + getPosition() + " " + item);
    viewHolderListener.onIndexChanged(getPosition());
    selectedPosition = getPosition();
    view.setBackgroundColor(Color.CYAN); 
    selectedListItem = view;
}

Ini dia onBindViewHolder:

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {   
    viewHolder.setItem(fruitsData[position]);
    if(selectedPosition == position)
        viewHolder.itemView.setBackgroundColor(Color.CYAN);    
    else
        viewHolder.itemView.setBackgroundColor(Color.RED);

}
pengguna65721
sumber
Menggunakan tampilan yang fokus bukan ide yang baik untuk mencoba melacak item yang dipilih. Periksa jawaban saya untuk solusi lengkap
Greg Ennis
semoga b bantuan ini kamu amolsawant88.blogspot.in/2015/08/...
Amol Sawant 96 Kuli
Pilihan item tampilan pendaur ulang: stackoverflow.com/a/38501676/2648035
Alok Omkar
Ini sulit untuk diikuti ketika Anda tidak memiliki apa-apa yang sudah berfungsi , yang buruk karena jawaban bersama dan tidak menentukan banyak tentang apa yang terjadi di mana.
FirstOne

Jawaban:

61

Saya menulis kelas adaptor dasar untuk secara otomatis menangani pemilihan item dengan RecyclerView. Turunkan adaptor Anda darinya dan gunakan daftar status yang dapat ditarik dengan state_selected, seperti yang akan Anda lakukan dengan tampilan daftar.

Saya punya Posting Blog Di Sini tentang itu, tetapi di sini adalah kodenya:

public abstract class TrackSelectionAdapter<VH extends TrackSelectionAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {
    // Start with first item selected
    private int focusedItem = 0;

    @Override
    public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);

        // Handle key up and key down and attempt to move selection
        recyclerView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();

                // Return false if scrolled to the bounds and allow focus to move off the list
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
                        return tryMoveSelection(lm, 1);
                    } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
                        return tryMoveSelection(lm, -1);
                    }
                }

                return false;
            }
        });
    }

    private boolean tryMoveSelection(RecyclerView.LayoutManager lm, int direction) {
        int tryFocusItem = focusedItem + direction;

        // If still within valid bounds, move the selection, notify to redraw, and scroll
        if (tryFocusItem >= 0 && tryFocusItem < getItemCount()) {
            notifyItemChanged(focusedItem);
            focusedItem = tryFocusItem;
            notifyItemChanged(focusedItem);
            lm.scrollToPosition(focusedItem);
            return true;
        }

        return false;
    }

    @Override
    public void onBindViewHolder(VH viewHolder, int i) {
        // Set selected state; use a state list drawable to style the view
        viewHolder.itemView.setSelected(focusedItem == i);
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(View itemView) {
            super(itemView);

            // Handle item click and set the selection
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Redraw the old selection and the new
                    notifyItemChanged(focusedItem);
                    focusedItem = getLayoutPosition();
                    notifyItemChanged(focusedItem);
                }
            });
        }
    }
} 
Greg Ennis
sumber
mRecyclerViewtidak dinyatakan di mana pun. Haruskah kita hanya meneruskannya sebagai parameter di konstruktor dan menyimpannya di bidang?
Pedro
1
Maaf soal itu. Adaptor saya adalah kelas dalam fragmen saya sehingga memiliki akses ke bidang tampilan pendaur ulang. Kalau tidak, ya Anda bisa meneruskannya sebagai parameter. Atau bahkan lebih baik, pegang onRecyclerViewAttacheddan simpan dalam variabel anggota di sana.
Greg Ennis
1
Jawaban yang bagus, tetapi mengapa menggunakan getChildPosition ()? Ada metode lain developer.android.com/reference/android/support/v7/widget/…
skyfishjy
Maaf, maksud Anda, saya hanya perlu mengimpor TrackSelectionAdapterkelas Anda dan menggunakannya di daftar saya? Bagaimana cara "mendapatkan adaptor saya dari kelas Anda"? Bisakah Anda menjawab pertanyaan saya? Saya terjebak sangat dalam: stackoverflow.com/questions/29695811/…
Cristiano Colacillo
2
Saya mencoba ini dan menemukan dua panggilan back-to-back untuk NotifyItemChanged () membunuh kemiripan menggulir halus pada perangkat keras yang lebih lambat. Sangat buruk di Fire TV sebelum pembaruan Lollipop
Redshirt
158

Ini adalah cara sederhana untuk melakukannya.

Memiliki private int selectedPos = RecyclerView.NO_POSITION;di kelas Adapter RecyclerView, dan di bawah metode onBindViewHolder coba:

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {   
    viewHolder.itemView.setSelected(selectedPos == position);

}

Dan di acara OnClick Anda, modifikasi:

@Override
public void onClick(View view) {
     notifyItemChanged(selectedPos);
     selectedPos = getLayoutPosition();
     notifyItemChanged(selectedPos); 
}

Bekerja seperti pesona untuk Laci Navigasi dan Adaptor Item RecyclerView lainnya.

Catatan: Pastikan untuk menggunakan warna latar belakang dalam tata letak Anda menggunakan pemilih seperti colabug yang diklarifikasi:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@color/pressed_color" android:state_pressed="true"/>
  <item android:drawable="@color/selected_color" android:state_selected="true"/>
  <item android:drawable="@color/focused_color" android:state_focused="true"/>
</selector>

Jika tidak, setSelected (..) tidak akan melakukan apa-apa, menjadikan solusi ini tidak berguna.

zIronManBox
sumber
12
Saya menggunakan solusi ini dengan latar belakang drawable / selector yang diatur pada tampilan baris saya: <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@color/pressed_color" android:state_pressed="true"/> <item android:drawable="@color/selected_color" android:state_selected="true"/> <item android:drawable="@color/focused_color" android:state_focused="true"/> </selector>
colabug
1
@zIronManBox: bagus, cara sederhana! Apakah "selectedPosition" dalam metode onClick seharusnya "selectedPos"?
AJW
3
getLayoutPosition () tidak tersedia oleh saya.
ka3ak
3
Jangan hanya menggunakan -1, gunakan RecyclerView.NO_POSITION; (yaitu -1)
Martin Marconcini
8
@ ka3ak: getLayoutPosition adalah metode kelas ViewHolder yang objeknya dilewatkan sebagai parameter pertama dalam metode bind view. Jadi itu dapat diakses olehvieHolder.getLayoutPosition
Tushar Kathuria
129

UPDATE [26 / Jul / 2017]:

Seperti yang disebutkan oleh Pawan dalam komentar tentang peringatan IDE tentang tidak menggunakan posisi tetap itu, saya baru saja memodifikasi kode saya seperti di bawah ini. Pendengar klik dipindahkan ke ViewHolder, dan di sana saya mendapatkan posisi menggunakan getAdapterPosition()metode

int selected_position = 0; // You have to set this globally in the Adapter class

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    Item item = items.get(position);

    // Here I am just highlighting the background
    holder.itemView.setBackgroundColor(selected_position == position ? Color.GREEN : Color.TRANSPARENT);
}

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    public ViewHolder(View itemView) {
        super(itemView);
        itemView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        // Below line is just like a safety check, because sometimes holder could be null,
        // in that case, getAdapterPosition() will return RecyclerView.NO_POSITION
        if (getAdapterPosition() == RecyclerView.NO_POSITION) return;

        // Updating old as well as new positions
        notifyItemChanged(selected_position);
        selected_position = getAdapterPosition();
        notifyItemChanged(selected_position);

        // Do your another stuff for your onClick
    }
}

Semoga ini bisa membantu.

Temui Vora
sumber
Itu luar biasa! Saya agak bingung, bisakah Anda membantu saya menerapkan cara, memperluas jawaban ini, bagaimana melakukan apa pun yang Anda lakukan dalam pernyataan lain ketika Anda mengklik pada item yang sudah dipilih, sehingga Anda tidak memilihnya: else { holder.itemView.setBackgroundColor (Color.TRANSPARENT); }
iBobb
Tentu saja ya. @ iBobb. Untuk item TIDAK TERPILIH, yaitu posisinya yang tidak sama dengan variabel 'selected_position' kami harus NO latar belakang. Saya menggunakan ini karena RecyclerView, seperti namanya, mendaur ulang setiap item, sehingga ia menetapkan latar belakang HIJAU untuk baris acak sementara kami SCROLL, itulah mengapa saya menempatkan bagian ELSE itu. (Anda bisa mencobanya dengan mengomentari bagian ELSE itu, dan kemudian gulir tampilan Pendaur Ulang Anda)
Temui Vora
Saya tidak berpikir Anda mengerti pertanyaan saya. Anda tahu di aplikasi apa pun, ketika Anda memilih sesuatu yang disorot. Jika Anda memegangnya lagi, itu akan terpilih. Bagaimana kita menerapkannya? Saat ini kami hanya dapat memilih menggunakan kode Anda dan menyentuh dan menahan item yang sama tidak melakukan apa-apa.
iBobb
Itu mungkin, tetapi untuk itu Anda harus mengganti onLongClickListener dan bukannya onClickListener, saat ini saya sibuk sehingga tidak dapat memberikan kode lengkap, tetapi pada dasarnya Anda harus melakukannya dengan cara itu.
Temui Vora
Untuk beberapa alasan butuh waktu untuk menyorot setelah klik, sekitar 100 hingga 200 milidetik. Ada yang tahu kenapa? Saya mengalami ini pada emulator dan telepon lolipop saya
suku
14

Implementasi Anda kemungkinan berhasil jika Anda menggulir konten keluar dari tampilan lalu kembali ke tampilan. Saya mengalami masalah serupa ketika saya menemukan pertanyaan Anda.

Cuplikan file berikut ini berfungsi untuk saya. Implementasi saya ditujukan pada banyak pilihan, tetapi saya melakukan hack di sana untuk memaksa seleksi tunggal. (* 1)

// an array of selected items (Integer indices) 
private final ArrayList<Integer> selected = new ArrayList<>();

// items coming into view
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    // each time an item comes into view, its position is checked
    // against "selected" indices
    if (!selected.contains(position)){
        // view not selected
        holder.parent.setBackgroundColor(Color.LTGRAY);
    }
    else
        // view is selected
        holder.parent.setBackgroundColor(Color.CYAN);
}

// selecting items
@Override
public boolean onLongClick(View v) {

        // set color immediately.
        v.setBackgroundColor(Color.CYAN);

        // (*1)
        // forcing single selection here
        if (selected.isEmpty()){
            selected.add(position);
        }else {
            int oldSelected = selected.get(0);
            selected.clear();
            selected.add(position);
            // we do not notify that an item has been selected
            // because that work is done here.  we instead send
            // notifications for items to be deselected
            notifyItemChanged(oldSelected);
        }
        return false;
}

Seperti dicatat dalam pertanyaan terkait ini , pengaturan pendengar untuk viewHolders harus dilakukan di onCreateViewHolder. Saya lupa menyebutkan ini sebelumnya.

Dustin Charles
sumber
6

Saya pikir, saya telah menemukan tutorial terbaik tentang cara menggunakan RecyclerView dengan semua fungsi dasar yang kita butuhkan (single + multiselection, highlight, ripple, klik dan hapus di multiselection, dll ...).

Ini dia -> http://enoent.fr/blog/2015/01/18/recyclerview-basics/

Berdasarkan hal itu, saya dapat membuat perpustakaan "FlexibleAdapter", yang memperluas SelectableAdapter. Saya pikir ini harus menjadi tanggung jawab Adaptor, sebenarnya Anda tidak perlu menulis ulang fungsi dasar Adaptor setiap kali, biarkan perpustakaan untuk melakukannya, sehingga Anda bisa menggunakan kembali implementasi yang sama.

Adaptor ini sangat cepat, berfungsi di luar kotak (Anda tidak perlu memperpanjangnya); Anda menyesuaikan item untuk setiap jenis tampilan yang Anda butuhkan; ViewHolder sudah ditentukan sebelumnya: peristiwa umum sudah diterapkan: klik tunggal dan panjang; itu mempertahankan keadaan setelah rotasi dan banyak lagi .

Silakan melihat dan merasa bebas untuk mengimplementasikannya di proyek Anda.

https://github.com/davideas/FlexibleAdapter

Wiki juga tersedia.

Davideas
sumber
Kerja bagus menulis adaptor itu, sepertinya sangat berguna. Satu-satunya hal adalah bahwa itu benar-benar memerlukan beberapa contoh dasar dan dokumentasi, saya merasa sedikit membingungkan untuk membuatnya dan berjalan. Berpotensi bagus!
Voy
Ya, saya tidak menemukan informasi yang cukup di sana untuk memulai. Tidak dapat menemukan referensi API meskipun kode tampaknya dikomentari dengan itu dalam pikiran. Aplikasi sampel tampaknya - walaupun luas dan informatif - sangat sulit untuk dipahami tanpa sepengetahuan perpustakaan sebelumnya. Semua use case terikat bersama-sama, ada sedikit indikasi tentang apa yang menunjukkan apa, kelas digunakan kembali di berbagai skenario yang menyebabkan mereka kelebihan informasi. Saya membuat masalah baru dengan saran-saran ini di sini: github.com/davideas/FlexibleAdapter/issues/120
Voy
Wiki telah sepenuhnya ditulis ulang dan sedang dalam proses penyelesaian.
Davideas
6

Lihat solusi saya. Saya kira Anda harus mengatur posisi yang dipilih pada tempatnya dan memberikannya sebagai Tag of View. Tampilan harus diatur dalam metode onCreateViewHolder (...). Ada juga tempat yang benar untuk mengatur pendengar untuk tampilan seperti OnClickListener atau LongClickListener.

Silakan lihat contoh di bawah ini dan baca komentar untuk kode.

public class MyListAdapter extends RecyclerView.Adapter<MyListAdapter.ViewHolder> {
    //Here is current selection position
    private int mSelectedPosition = 0;
    private OnMyListItemClick mOnMainMenuClickListener = OnMyListItemClick.NULL;

    ...

    // constructor, method which allow to set list yourObjectList

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //here you prepare your view 
        // inflate it
        // set listener for it
        final ViewHolder result = new ViewHolder(view);
        final View view =  LayoutInflater.from(parent.getContext()).inflate(R.layout.your_view_layout, parent, false);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //here you set your current position from holder of clicked view
                mSelectedPosition = result.getAdapterPosition();

                //here you pass object from your list - item value which you clicked
                mOnMainMenuClickListener.onMyListItemClick(yourObjectList.get(mSelectedPosition));

                //here you inform view that something was change - view will be invalidated
                notifyDataSetChanged();
            }
        });
        return result;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        final YourObject yourObject = yourObjectList.get(position);

        holder.bind(yourObject);
        if(mSelectedPosition == position)
            holder.itemView.setBackgroundColor(Color.CYAN);
        else
            holder.itemView.setBackgroundColor(Color.RED);
    }

    // you can create your own listener which you set for adapter
    public void setOnMainMenuClickListener(OnMyListItemClick onMyListItemClick) {
        mOnMainMenuClickListener = onMyListItemClick == null ? OnMyListItemClick.NULL : onMyListItemClick;
    }

    static class ViewHolder extends RecyclerView.ViewHolder {


        ViewHolder(View view) {
            super(view);
        }

        private void bind(YourObject object){
            //bind view with yourObject
        }
    }

    public interface OnMyListItemClick {
        OnMyListItemClick NULL = new OnMyListItemClick() {
            @Override
            public void onMyListItemClick(YourObject item) {

            }
        };

        void onMyListItemClick(YourObject item);
    }
}
Konrad Krakowiak
sumber
Apakah menurut Anda mSelectedPosition dapat dinyatakan sebagai statis untuk mempertahankan perubahan konfigurasi?
Sathesh
Tidak! itu salah untuk membuatnya sebagai statis itu sangat salah
Konrad Krakowiak
Ya. itu berbahaya. Tetapi untuk mempertahankan perubahan konfigurasi (khususnya rotasi layar), kita dapat mendeklarasikan variabel ini dalam aktivitas dan mendapatkannya dari sana, bukan?
Sathesh
Ada banyak solusi ... Anda dapat menggunakan pengambil dan penyetel, Anda dapat membuat metode Anda sendiri saveInstanceState untuk adaptor dan menyebutnya di saveInstanceState dari Activity / Fragment
Konrad Krakowiak
4

tidak ada pemilih di RecyclerView seperti ListView dan GridView tetapi Anda mencoba di bawah ini berfungsi untuk saya

buat pemilih yang bisa ditarik seperti di bawah ini

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
<item android:state_pressed="true">
   <shape>
         <solid android:color="@color/blue" />
   </shape>
</item>

<item android:state_pressed="false">
    <shape>
       <solid android:color="@android:color/transparent" />
    </shape>
</item>
</selector>

kemudian atur gambar ini sebagai latar belakang tata letak baris RecyclerView Anda sebagai

android:background="@drawable/selector"
amodkanthe
sumber
2
Ini tidak ada gunanya. Itu hanya akan mengubah warna saat menekan waktu.
Leo Droidcoder
4

Keputusan dengan Antarmuka dan Callback. Buat Antarmuka dengan status pilih dan batalkan pilihan:

public interface ItemTouchHelperViewHolder {
    /**
     * Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.
     * Implementations should update the item view to indicate it's active state.
     */
    void onItemSelected();


    /**
     * Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item
     * state should be cleared.
     */
    void onItemClear();
}

Implementasikan antarmuka di ViewHolder:

   public static class ItemViewHolder extends RecyclerView.ViewHolder implements
            ItemTouchHelperViewHolder {

        public LinearLayout container;
        public PositionCardView content;

        public ItemViewHolder(View itemView) {
            super(itemView);
            container = (LinearLayout) itemView;
            content = (PositionCardView) itemView.findViewById(R.id.content);

        }

               @Override
    public void onItemSelected() {
        /**
         * Here change of item
         */
        container.setBackgroundColor(Color.LTGRAY);
    }

    @Override
    public void onItemClear() {
        /**
         * Here change of item
         */
        container.setBackgroundColor(Color.WHITE);
    }
}

Jalankan perubahan status pada Callback:

public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {

    private final ItemTouchHelperAdapter mAdapter;

    public ItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
        this.mAdapter = adapter;
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = ItemTouchHelper.END;
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        ...
    }

    @Override
    public void onSwiped(final RecyclerView.ViewHolder viewHolder, int direction) {
        ...
    }

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            if (viewHolder instanceof ItemTouchHelperViewHolder) {
                ItemTouchHelperViewHolder itemViewHolder =
                        (ItemTouchHelperViewHolder) viewHolder;
                itemViewHolder.onItemSelected();
            }
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        if (viewHolder instanceof ItemTouchHelperViewHolder) {
            ItemTouchHelperViewHolder itemViewHolder =
                    (ItemTouchHelperViewHolder) viewHolder;
            itemViewHolder.onItemClear();
        }
    }   
}

Buat RecyclerView dengan Callback (contoh):

mAdapter = new BuyItemsRecyclerListAdapter(MainActivity.this, positionsList, new ArrayList<BuyItem>());
positionsList.setAdapter(mAdapter);
positionsList.setLayoutManager(new LinearLayoutManager(this));
ItemTouchHelper.Callback callback = new ItemTouchHelperCallback(mAdapter);
mItemTouchHelper = new ItemTouchHelper(callback);
mItemTouchHelper.attachToRecyclerView(positionsList);

Lihat lebih banyak di artikel iPaulPro: https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.6gh29uaaz

Mykola Tychyna
sumber
3

ini solusi saya, Anda dapat mengatur pada item (atau grup) dan membatalkan pilihan dengan klik lain:

 private final ArrayList<Integer> seleccionados = new ArrayList<>();
@Override
    public void onBindViewHolder(final ViewHolder viewHolder, final int i) {
        viewHolder.san.setText(android_versions.get(i).getAndroid_version_name());
        if (!seleccionados.contains(i)){ 
            viewHolder.inside.setCardBackgroundColor(Color.LTGRAY);
        }
        else {
            viewHolder.inside.setCardBackgroundColor(Color.BLUE);
        }
        viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (seleccionados.contains(i)){
                    seleccionados.remove(seleccionados.indexOf(i)); 
                    viewHolder.inside.setCardBackgroundColor(Color.LTGRAY);
                } else { 
                    seleccionados.add(i);
                    viewHolder.inside.setCardBackgroundColor(Color.BLUE);
                }
            }
        });
    }
Santiago Castellano
sumber
2

Saya memiliki Masalah yang sama dan saya menyelesaikannya dengan cara berikut:

File xml yang digunakan untuk membuat Baris di dalam createViewholder, cukup tambahkan baris di bawah ini:

 android:clickable="true"
 android:focusableInTouchMode="true"
 android:background="?attr/selectableItemBackgroundBorderless"

ATAU Jika Anda menggunakan frameLayout sebagai induk dari item baris, maka:

android:clickable="true"
android:focusableInTouchMode="true"
android:foreground="?attr/selectableItemBackgroundBorderless"

Dalam kode java di dalam view holder tempat Anda menambahkan pada pendengar klik:

@Override
   public void onClick(View v) {

    //ur other code here
    v.setPressed(true);
 }
Vikas Tiwari
sumber
1

Saya tidak dapat menemukan solusi yang baik di web untuk masalah ini dan menyelesaikannya sendiri. Ada banyak orang yang menderita masalah ini. Jadi saya ingin membagikan solusi saya di sini.

Saat menggulir, baris didaur ulang. Dengan demikian, kotak centang yang dicentang dan baris yang disorot tidak berfungsi dengan benar. Saya memecahkan masalah ini dengan menulis kelas adaptor di bawah ini.

Saya juga menerapkan proyek penuh. Dalam proyek ini, Anda dapat memilih beberapa kotak centang. Baris termasuk kotak centang yang dipilih disorot. Dan yang lebih penting, ini tidak hilang saat menggulir. Anda dapat mengunduhnya dari tautan:

https://www.dropbox.com/s/ssm58w62gw32i29/recyclerView_checkbox_highlight.zip?dl=0

    public class RV_Adapter extends RecyclerView.Adapter<RV_Adapter.ViewHolder> {
        public ArrayList<String> list;
        boolean[] checkBoxState;
        MainActivity mainActivity;
        MyFragment myFragment;
        View firstview;

        private Context context;

        FrameLayout framelayout;

        public RV_Adapter() {

      }

        public RV_Adapter(Context context, MyFragment m, ArrayList<String> list ) {
          this.list = list;
          myFragment = m;
          this.context = context;
          mainActivity = (MainActivity) context;
          checkBoxState = new boolean[list.size()];
          // relativeLayoutState = new boolean[list.size()];
        }

        public class ViewHolder extends RecyclerView.ViewHolder  {
            public TextView textView;
            public CheckBox checkBox;
            RelativeLayout relativeLayout;
            MainActivity mainActivity;
            MyFragment myFragment;
            public ViewHolder(View v,MainActivity mainActivity,MyFragment m) {
                super(v);
                textView = (TextView) v.findViewById(R.id.tv_foodname);
                /**/
                checkBox= (CheckBox) v.findViewById(R.id.checkBox);
                relativeLayout = (RelativeLayout)v.findViewById(R.id.relativelayout);
                this.mainActivity = mainActivity;
                this.myFragment = m;
                framelayout = (FrameLayout) v.findViewById(R.id.framelayout);
                framelayout.setOnLongClickListener(m);
            }

        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            firstview = inflater.inflate(R.layout.row, parent, false);
            return new ViewHolder(firstview,mainActivity, myFragment);
        }

        @Override
        public void onBindViewHolder( final ViewHolder holder,  final int position) {

            holder.textView.setText(list.get(position));

            holder.itemView.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {

              }
            });

            // When action mode is active, checkboxes are displayed on each row, handle views(move icons) on each row are disappered.
            if(!myFragment.is_in_action_mode)
            {

              holder.checkBox.setVisibility(View.GONE);
            }
            else
            {
              holder.checkBox.setVisibility(View.VISIBLE);
              holder.checkBox.setChecked(false);
            }

              holder.checkBox.setTag(position);

              holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
                @Override
                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                  if(compoundButton.isPressed()) // ekrandan kaybolan checkbox'lar otomatik olarak state degistiriyordu ve bu listener method cagiriliyordu, bunu onlemek icin isPressed() method'u ile kullanici mi basmis diye kontrol ediyorum.
                  {
                    int getPosition = (Integer) compoundButton.getTag();  // Here we get the position that we have set for the checkbox using setTag.
                    checkBoxState[getPosition] = compoundButton.isChecked(); // Set the value of checkbox to maintain its state.
                    //relativeLayoutState[getPosition] = compoundButton.isChecked();

                  if(checkBoxState[getPosition] && getPosition == position )
                    holder.relativeLayout.setBackgroundResource(R.color.food_selected); /** Change background color of the selected items in list view  **/
                  else
                    holder.relativeLayout.setBackgroundResource(R.color.food_unselected); /** Change background color of the selected items in list view  **/
                    myFragment.prepareselection(compoundButton, getPosition, holder.relativeLayout);

                  }
                }
              });
              holder.checkBox.setChecked(checkBoxState[position]);

              if(checkBoxState[position]  )
                holder.relativeLayout.setBackgroundResource(R.color.food_selected); /** Change background color of the selected items in list view  **/
              else
                holder.relativeLayout.setBackgroundResource(R.color.food_unselected);
        }



        @Override
        public int getItemCount() {
            return list.size();
        }

        public void updateList(ArrayList<String> newList){
          this.list = newList;
          checkBoxState = new boolean[list.size()+1];
        }

      public void resetCheckBoxState(){
        checkBoxState = null;
        checkBoxState = new boolean[list.size()];
      }

    }

Tangkapan layar aplikasi:

screen1 screen2 screen3 screen4

oiyio
sumber
-1

Setel private int selected_position = -1;untuk mencegah item apa pun dipilih pada awal.

 @Override
 public void onBindViewHolder(final OrdersHolder holder, final int position) {
    final Order order = orders.get(position);
    holder.bind(order);
    if(selected_position == position){
        //changes background color of selected item in RecyclerView
        holder.itemView.setBackgroundColor(Color.GREEN);
    } else {
        holder.itemView.setBackgroundColor(Color.TRANSPARENT);
        //this updated an order property by status in DB
        order.setProductStatus("0");
    }
    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //status switch and DB update
            if (order.getProductStatus().equals("0")) {
                order.setProductStatus("1");
                notifyItemChanged(selected_position);
                selected_position = position;
                notifyItemChanged(selected_position);
             } else {
                if (order.getProductStatus().equals("1")){
                    //calls for interface implementation in
                    //MainActivity which opens a new fragment with 
                    //selected item details 
                    listener.onOrderSelected(order);
                }
             }
         }
     });
}
LeonD
sumber
-1

Hanya menambahkan android:background="?attr/selectableItemBackgroundBorderless" berfungsi jika Anda tidak memiliki warna latar belakang, tetapi jangan lupa untuk menggunakan metode setSelected. Jika Anda memiliki warna latar belakang yang berbeda, saya hanya menggunakan ini (saya menggunakan data-binding);

Setel Dipilih di fungsi onClick

b.setIsSelected(true);

Dan tambahkan ini ke xml;

android:background="@{ isSelected ? @color/{color selected} : @color/{color not selected} }"
Mohamed
sumber
-1

Jadikan pemilih:

 <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/Green_10" android:state_activated="true" />
    <item android:drawable="@color/Transparent" />
</selector>

Atur sebagai latar belakang pada tata letak item daftar Anda

   <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:background="@drawable/selector_attentions_list_item"
        android:layout_width="match_parent"
        android:layout_height="64dp">

Dalam adaptor Anda, tambahkan OnClickListener ke tampilan (metode onBind)

 @Suppress("UNCHECKED_CAST")
    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bindItems(item: T) {
            initItemView(itemView, item)
            itemView.tag = item
            if (isClickable) {
                itemView.setOnClickListener(onClickListener)
            }
        }
    }

Di acara onClick, aktifkan tampilan:

 fun onItemClicked(view: View){
        view.isActivated = true
    }
i_tanova
sumber
2
Ini memiliki 2 masalah: 1) Anda belum memberikan cara apa pun untuk membatalkan aktivasi item lain dalam pendaur ulang. 2) Tampilan digunakan kembali. Jadi ada kemungkinan bahwa tampilan yang diaktifkan yang sama akan terlihat di urutan ketika digulir.
Anup Sharma