CheckBox w RecyclerView sprawdza różne elementy

93

Oto kod XML dla moich elementów w RecyclerView

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cvItems"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"
    android:layout_margin="2dp"
    card_view:cardElevation="0dp"
    card_view:contentPadding="0dp"
    card_view:cardBackgroundColor="#FFFFFF"
    >

    <LinearLayout
        android:orientation="horizontal"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent">
        <TextView
            android:layout_width="0dip"
            android:layout_height="match_parent"
            android:layout_weight="0.8"
            android:id="@+id/tvContent"
            android:textSize="15dp"
            android:paddingLeft="5dp"
            android:paddingRight="5dp" />
        <CheckBox
            android:id="@+id/cbSelect"
            android:layout_width="0dip"
            android:layout_weight="0.2"
            android:layout_height="match_parent"
            android:button="@drawable/cb_checked"
            android:gravity="center_horizontal"
            android:textAlignment="center"
            android:layout_gravity="center_horizontal" />
    </LinearLayout>
</android.support.v7.widget.CardView>

A oto adapter RecyclerView, który zawyża powyższy układ dla każdego z jego elementów:

public class AdapterTrashIncome extends RecyclerView.Adapter<AdapterTrashIncome.ViewHolder> {

    private ArrayList<ObjectIncome> myItems = new ArrayList<>();

    public AdapterTrashIncome(ArrayList<ObjectIncome> getItems, Context context){
        try {
            mContext = context;
            myItems = getItems;
        }catch (Exception e){
            Log.e(FILE_NAME, "51: " + e.toString());
            e.printStackTrace();
        }
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView tvContent;
        public CheckBox cbSelect;

        public ViewHolder(View v) {
            super(v);
            tvContent = (TextView) v.findViewById(R.id.tvContent);
            cbSelect = (CheckBox) v.findViewById(R.id.cbSelect);
        }
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        final ObjectIncome objIncome = myItems.get(position);
        String content = "<b>lalalla</b>";
        holder.tvContent.setText(Html.fromHtml(content));
    }
}

Problem polega na tym, że powiedzmy, że mam 10 elementów wewnątrz RecyclerView. Kiedy zaznaczyłem pole wyboru w pozycji 1, 2, 3, przewijam w dół RecyclerView, nagle niektóre inne pozycje, np. Pozycje 8,9, są zaznaczone. A kiedy przewijam ponownie w górę, zaznaczone są pozycje 1 i 3, ale nie pozycja 2. Masz pojęcie, dlaczego tak się stało?

Jestem za
źródło
Spróbuj użyć tej biblioteki , zobacz ViewStates. Pomaga zachować stan podczas przewijania.
Witalij

Odpowiedzi:

167

To oczekiwane zachowanie. Nie wybierasz lub nie zaznaczasz pola wyboru. Wybierasz jeden, a posiadacz widoku zachowuje go. Możesz dodać zmienną boolowską do obiektu ObjectIncome i zachować status wyboru elementu.

Możesz spojrzeć na mój przykład. Możesz zrobić coś takiego:

public class AdapterTrashIncome extends RecyclerView.Adapter<AdapterTrashIncome.ViewHolder> {

    private ArrayList<ObjectIncome> myItems = new ArrayList<>();

    public AdapterTrashIncome(ArrayList<ObjectIncome> getItems, Context context){
        try {
            mContext = context;
            myItems = getItems;
            }catch (Exception e){
            Log.e(FILE_NAME, "51: " + e.toString());
            e.printStackTrace();
        }
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView tvContent;
        public CheckBox cbSelect;

        public ViewHolder(View v) {
            super(v);
            tvContent = (TextView) v.findViewById(R.id.tvContent);
            cbSelect = (CheckBox) v.findViewById(R.id.cbSelect);
        }
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        final ObjectIncome objIncome = myItems.get(position);
        String content = "<b>lalalla</b>";
        holder.tvContent.setText(Html.fromHtml(content));

        //in some cases, it will prevent unwanted situations
        holder.cbSelect.setOnCheckedChangeListener(null);

        //if true, your checkbox will be selected, else unselected
        holder.cbSelect.setChecked(objIncome.isSelected());

        holder.cbSelect.setOnCheckedChangeListener(new OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    //set your object's last status
                    objIncome.setSelected(isChecked);
            }
        });

    }
}
Oğuzhan Döngül
źródło
19
To nie zadziałało. Musisz napisać holder.cbSelect.setOnCheckedChangeListener(null);wcześniejholder.cbSelect.setChecked(objIncome.isSelected())
Jemshit Iskenderov
2
czy istnieje jakikolwiek powód, dla którego ustawienie holder.cbSelect.setOnCheckedChangeListener (null); Pracuje?
Deb
4
@oguzhand cześć Wypróbowałem twoje rozwiązanie, ale nie działa w żaden sposób: z ustawieniem słuchacza na null lub bez niego.
Abbas
3
@oguzhand Oto kod z onBindViewHolder. @Override public void onBindViewHolder(final ItemHolder holder, int position) { holder.checkBox.setOnCheckedChangeListener(null); holder.checkBox.setSelected(list.get(position).isSelected()); holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { list.get(holder.getAdapterPosition()).setSelected(isChecked); } });
Abbas
1
@Suisse musisz zachować status pola wyboru w obiekcie, ponieważ ViewHolder jest tylko posiadaczem. Jeśli masz 100 elementów, masz tylko około 6-7 (w zależności od ekranu i rozmiaru układu) ViewHolders i używasz tych wszystkich obiektów w jednym cyklu.
Oğuzhan Döngül
22

Krótko mówiąc, wynika to z recyklingu widoków i ponownego ich wykorzystania!

jak możesz tego uniknąć:

1.Wybierz, onBindViewHolderczy powinieneś zaznaczyć lub odznaczyć pola. nie zapomnij umieścić zarówno if, jak i else

if (...)
    holder.cbSelect.setChecked(true);
else
    holder.cbSelect.setChecked(false);
  1. Umieść nasłuchiwanie dla pola wyboru! za każdym razem, gdy zmieniły się jego zaznaczone statuetki, zaktualizuj odpowiedni obiekt również w swojej myItemstablicy! więc za każdym razem, gdy wyświetlany jest nowy widok, odczytywany jest najnowszy posąg obiektu.
Omid Heshmatinia
źródło
Twój drugi punkt był kluczem. Chociaż najlepiej sprawdza się w sytuacji, gdy w początkowym zbiorze danych znajdują się również informacje o sprawdzonym stanie (tak jest w moim przypadku)
Attila Orosz
1
to jest bardziej prosta i poprawna odpowiedź. SetCheck for BOTH true i false w onBindViewHolder jest kluczem
Beeing Jk
W moim przypadku muszę zapisać dane w modelu danych z domyślną wartością isChecked falsedla wszystkich zestawów danych na starcie, a następnie onCheckChangedzaktualizowałem isCheckedwartość do truelub falsei zgodnie z odpowiedzią zaimplementuj, że sprawdzam, czy zaznaczono, czy nie.
Ali Tamoor
20

UŻYWAJ TEGO TYLKO, JEŚLI MASZ OGRANICZONĄ LICZBĘ ELEMENTÓW W WIDOKU ODBIORCY.
Próbowałem użyć wartości logicznej w modelu i zachować status pola wyboru, ale to nie pomogło w moim przypadku. U mnie zadziałało this.setIsRecyclable (false);

public class ComponentViewHolder extends RecyclerView.ViewHolder {
    public MyViewHolder(View itemView) {
        super(itemView);
        ....
        this.setIsRecyclable(false);
    }

Więcej wyjaśnień na ten temat można znaleźć tutaj https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#isRecyclable ()

UWAGA: To jest obejście. Aby użyć go poprawnie, możesz odwołać się do dokumentu, w którym stwierdza się, że „Wywołania setIsRecyclable () powinny być zawsze sparowane (jedno wywołanie setIsRecyclabe (false) powinno być zawsze dopasowane do późniejszego wywołania setIsRecyclable (true)). Pary wywołań mogą być zagnieżdżane , ponieważ stan jest wewnętrznie liczony jako odniesienie. " Nie wiem, jak to zrobić w kodzie, jeśli ktoś może podać więcej kodu na ten temat.

Rana Ranvijay Singh
źródło
czy możesz wyjaśnić, jak go używać?
UserName_Untold
44
czy nie jest to marnowanie logiki stojącej za recyclinglerView?
Eren
3
Próbowałem tego z długą listą, która rozwiązała problem z losowym sprawdzaniem, ale kiedy przewijam w dół i ponownie przewijam w górę z długą listą, zaznaczone pola wyboru znikają :(
SonDang
2
Nie jest dobrym pomysłem uniemożliwienie odtworzenia widoku, ponieważ spowoduje to wyczerpanie pamięci i utratę większości zalet widoku recyklera.
Arthur
Zgadzam się z wami, @ eren130 i Arthurem. Edytowałem post i byłbym bardzo wdzięczny, gdybyśmy mogli wymyślić sposób użycia setIsRecyclable (true / false); prawidłowo.
Rana Ranvijay Singh
13

Po prostu dodaj dwie metody zastępowania RecyclerView

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

@Override
public int getItemViewType(int position) {
    return position;
}
Harish Reddy
źródło
2
Nie rób tego !! Omija mechanizm recyklingu recyklingu i straci cały sens jego używania.
Hanoch Moreno,
1
Nie, nie będzie, po prostu zwraca dokładną pozycję każdego przetworzonego widoku w uchwycie widoku.
Harish Reddy
1
Harish, może coś mi brakuje, ale o ile wiem, robiąc to, w rzeczywistości informujesz adapter, że liczba typów elementów to liczba elementów. Oznacza to, że żaden przedmiot nie może zostać poddany recyklingowi, ponieważ nie ma podobnego widoku. Jednak łatwo jest to przetestować. po prostu zaloguj się odwołanie viewHolder.itemView wewnątrz onBindViewHolder i sprawdź, czy istnieją dwa elementy viewHolders przechowujące to samo odwołanie do widoku. Test powinien znajdować się na długiej liście, aby system recyklingu został uruchomiony.
Hanoch Moreno
3
Działało bez zarzutu, uratowało mi dzień.
Kundan
5
w przypadku gdy masz więcej niż 100 produktów w recyclerview, to rozwiązanie będzie ładować wszystkie przedmioty na raz, może to spowodować OutOfMemoryException w przypadku gdy masz zdjęć środki inaczej jest to rozwiązanie idealne @Kundan
Harish Reddy
11

Możesz użyć klasy Model, aby śledzić pole wyboru każdego elementu recyklingu. Pełne odniesienie pochodzi z: RecyclerView Checkbox Android

setTag i getTag są używane do śledzenia stanu pola wyboru. Sprawdź pełne łącze referencyjne, aby uzyskać więcej informacji. Uczy również, jak wysłać zaznaczone elementy do NEXTACTIVITY .

Make Model

public class Model {

    private boolean isSelected;
    private String animal;

    public String getAnimal() {
        return animal;
    }

    public void setAnimal(String animal) {
        this.animal = animal;
    }

    public boolean getSelected() {
        return isSelected;
    }

    public void setSelected(boolean selected) {
        isSelected = selected;
    }
}

utwórz integer.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="btnplusview">1</integer>
    <integer name="btnpluspos">2</integer>
</resources>

Wreszcie adapter wygląda tak:

 import android.content.Context;
 import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
    import android.view.View;
 import android.view.ViewGroup;
 import android.widget.CheckBox;
 import android.widget.TextView;
 import android.widget.Toast;

 import java.util.ArrayList;


  public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> {

private LayoutInflater inflater;
public static ArrayList<Model> imageModelArrayList;
private Context ctx;

public CustomAdapter(Context ctx, ArrayList<Model> imageModelArrayList) {

    inflater = LayoutInflater.from(ctx);
    this.imageModelArrayList = imageModelArrayList;
    this.ctx = ctx;
}

@Override
public CustomAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = inflater.inflate(R.layout.rv_item, parent, false);
    MyViewHolder holder = new MyViewHolder(view);

    return holder;
}

@Override
public void onBindViewHolder(final CustomAdapter.MyViewHolder holder, int position) {

    holder.checkBox.setText("Checkbox " + position);
    holder.checkBox.setChecked(imageModelArrayList.get(position).getSelected());
    holder.tvAnimal.setText(imageModelArrayList.get(position).getAnimal());

   // holder.checkBox.setTag(R.integer.btnplusview, convertView);
    holder.checkBox.setTag(position);
    holder.checkBox.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Integer pos = (Integer) holder.checkBox.getTag();
            Toast.makeText(ctx, imageModelArrayList.get(pos).getAnimal() + " clicked!", Toast.LENGTH_SHORT).show();

            if (imageModelArrayList.get(pos).getSelected()) {
                imageModelArrayList.get(pos).setSelected(false);
            } else {
                imageModelArrayList.get(pos).setSelected(true);
            }
        }
    });


}

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

class MyViewHolder extends RecyclerView.ViewHolder {

    protected CheckBox checkBox;
    private TextView tvAnimal;

    public MyViewHolder(View itemView) {
        super(itemView);

        checkBox = (CheckBox) itemView.findViewById(R.id.cb);
        tvAnimal = (TextView) itemView.findViewById(R.id.animal);
    }

}

}

user6435056
źródło
3

Używając Kotlina, jedyną rzeczą, która rozwiązała ten problem dla mnie, było wyczyszczenie OnCheckedChangeListenerprzed ustawieniem zmiennej, a następnie utworzenie nowej wartości OnCheckedChangeListenerafter checked.

Wykonuję następujące czynności w moim RecyclerView.ViewHolder

task.setOnCheckedChangeListener(null)
task.isChecked = item.status
task.setOnCheckedChangeListener { _: CompoundButton, checked: Boolean ->
    item.status = checked
    ...
    do more stuff
    ...
}
just_user
źródło
To działa doskonale. Nie wiem dlaczego, ale to działa tylko wtedy, gdy ktoś używa KOTLIN!
Aditya S.
2

Jak wspomniano powyżej, sprawdzony stan obiektu powinien być zawarty we właściwościach obiektu. W niektórych przypadkach może zajść potrzeba zmiany stanu zaznaczenia obiektu przez kliknięcie samego obiektu i poinformowanie CheckBox o aktualnym stanie (zaznaczonym lub niezaznaczonym). Pole wyboru użyje wówczas stanu obiektu w aktualnej pozycji danego adaptera, która jest (domyślnie / w większości przypadków) pozycją elementu na liście.

Sprawdź poniższy fragment, może się przydać.

import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;

import java.io.File;
import java.io.IOException;
import java.util.List;

public class TakePicImageAdapter extends RecyclerView.Adapter<TakePicImageAdapter.ViewHolder>{
    private Context context;
    private List<Image> imageList;

    public TakePicImageAdapter(Context context, List<Image> imageList) {
        this.context = context;
        this.imageList = imageList;
    }

    @Override
    public TakePicImageAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(context).inflate(R.layout.image_item,parent,false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final TakePicImageAdapter.ViewHolder holder, final int position) {
        File file=new File(imageList.get(position).getPath());
        try {
            Bitmap bitmap= MediaStore.Images.Media.getBitmap(context.getContentResolver(), Uri.fromFile(file));
            holder.image.setImageBitmap(bitmap
            );
        } catch (IOException e) {
            e.printStackTrace();
        }
        holder.selectImage.setOnCheckedChangeListener(null);
        holder.selectImage.setChecked(imageList.get(position).isSelected());
        holder.selectImage.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                holder.selectImage.setChecked(isChecked);
                imageList.get(position).setSelected(isChecked);
            }
        });
        holder.image.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (imageList.get(position).isSelected())
                {
                    imageList.get(position).setSelected(false);
                    holder.selectImage.setChecked(false);
                }else
                {
                    imageList.get(position).setSelected(true);
                    holder.selectImage.setChecked(true);
                }
            }
        });

    }

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

    public class ViewHolder extends RecyclerView.ViewHolder {
        public ImageView image;public CheckBox selectImage;
        public ViewHolder(View itemView) {
            super(itemView);
            image=(ImageView)itemView.findViewById(R.id.image);
            selectImage=(CheckBox) itemView.findViewById(R.id.ch);

        }
    }
}

Gratien Asimbahwe
źródło
2

W moim przypadku to zadziałało.

@Override
public void onViewRecycled(MyViewHolder holder) {
    holder.checkbox.setChecked(false); // - this line do the trick
    super.onViewRecycled(holder);
}
tollestheanimal
źródło
2

Użyj tablicy do przechowywania stanu elementów

W adapterze użyj Map lub SparseBooleanArray (który jest podobny do mapy, ale jest parą klucz-wartość int i boolean) do przechowywania stanu wszystkich elementów z naszej listy elementów, a następnie użyj kluczy i wartości do porównania podczas przełączania stanu zaznaczenia

W adapterze utwórz plik SparseBooleanArray

// sparse boolean array for checking the state of the items

    private SparseBooleanArray itemStateArray= new SparseBooleanArray();

następnie w onClick()module obsługi kliknięcia elementu użyj stanu elementów w itemStateArray do sprawdzenia przed przełączeniem, oto przykład

        @Override
        public void onClick(View v) {
            int adapterPosition = getAdapterPosition();
            if (!itemStateArray.get(adapterPosition, false)) {
                mCheckedTextView.setChecked(true);
                itemStateArray.put(adapterPosition, true);
            }
            else  {
                mCheckedTextView.setChecked(false);
                itemStateArray.put(adapterPosition, false);
            }
        }

również użyj rzadkiej tablicy logicznej, aby ustawić stan zaznaczenia, gdy widok jest powiązany

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    holder.bind(position);
}

@Override
public int getItemCount() {
    if (items == null) {
        return 0;
    }
    return items.size();
}

 void loadItems(List<Model> tournaments) {
    this.items = tournaments;
    notifyDataSetChanged();
}


class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    CheckedTextView mCheckedTextView;

    ViewHolder(View itemView) {
        super(itemView);
        mCheckedTextView = (CheckedTextView) itemView.findViewById(R.id.checked_text_view);
        itemView.setOnClickListener(this);
    }

    void bind(int position) {
        // use the sparse boolean array to check
        if (!itemStateArray.get(position, false)) {
            mCheckedTextView.setChecked(false);}
        else {
            mCheckedTextView.setChecked(true);
        }
    }

i końcowy adapter będzie jak ten

Basi
źródło
1

Musisz oddzielić interakcje onBindViewHolder (logika) z CheckBox i interakcje użytkownika z polem wyboru. Użyłem OnCheckedChangeListener do interakcji użytkownika (oczywiście) i ViewHolder.bind () do logiki, dlatego przed skonfigurowaniem posiadacza należy ustawić zaznaczony odbiornik na null, a po przygotowaniu posiadacza - skonfiguruj sprawdzony odbiornik do interakcji z użytkownikiem.

boolean[] checkedStatus = new boolean[numberOfRows];

@Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
        final ViewHolderItem itemHolder = (ViewHolderItem) holder;

        //holder.bind should not trigger onCheckedChanged, it should just update UI
        itemHolder.checkBox.setOnCheckedChangeListener(null);

        itemHolder.bind(position);

        itemHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    checkedStatus[holder.getAdapterPosition()] = true;
                    performCheckedActions(); //your logic here
                } else {
                    checkedStatus[holder.getAdapterPosition()] = false;
                    performUncheckedActions(); //your logic here
                }
            }
        });
    }

public void bind(int position) {
            boolean checked = checkedStatus[position];
            if (checked) {
                checkBox.setChecked(false);
            } else {
                checkBox.setChecked(true);
            }
        }
Levor
źródło
1

Zalecam nie używać checkBox.setOnCheckedChangeListenerw recyclerViewAdapter. Ponieważ przy przewijaniu reclerView checkBox.setOnCheckedChangeListenerzostanie uruchomiony przez adapter. To nie jest bezpieczne . Zamiast tego użyj checkBox.setOnClickListenerdo interakcji z danymi wejściowymi użytkownika.

Na przykład:

     public void onBindViewHolder(final ViewHolder holder, int position) {
        /*
         .
         .
         .
         .
         .
         .
        */

        holder.checkBoxAdapterTasks.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean isChecked =  holder.checkBoxAdapterTasks.isChecked();
                if(isChecked){
                    //checkBox clicked and checked
                }else{
                    //checkBox clicked and unchecked
                }

            }
        });

    }
Eren
źródło
1

Problem tego rozwiązania, który znalazłem, polega na utworzeniu statycznej tablicy globalnej i użyciu jej w KLASIE ADAPERÓW "onBindViewHolder", w której stworzyłem wszystkie potrzebne globalne zmienne / obiekty.

public class RVAdapter extends RecyclerView.Adapter<RVAdapter.PersonViewHolder> {
private Context context;
public static class PersonViewHolder extends RecyclerView.ViewHolder {

    CardView cv;
    TextView question,category;
    TextView personAge;
    ImageView upvote;
    Button b1;
    public static int k;
    private int visibleThreshold = 5;
    public static int i=0;
     static int  check[]; //Static array
    PersonViewHolder(View itemView,int i) {
        super(itemView);
        if(i==PersonViewHolder.k)
        {
            b1=(Button)itemView.findViewById(R.id.loadmore);

        }
        else
        {
            cv = (CardView)itemView.findViewById(R.id.cv);
            question = (TextView)itemView.findViewById(R.id.question);
            category = (TextView)itemView.findViewById(R.id.text_categ);
            personAge = (TextView)itemView.findViewById(R.id.text1);
            upvote = (ImageView)itemView.findViewById(R.id.upvote);

        }

    }

}

Tutaj (W KONSTRUKTORZE KLASY RVADAPTER) podałem rozmiar tablicy równy rozmiarowi / liczbie elementów, które mam zamiar wyświetlić w widoku recyklera

List<Person> persons;

RVAdapter(List<Person> persons){
    this.persons = persons;
    PersonViewHolder.check=new int[persons.size()];
    PersonViewHolder.k=persons.size();
}

BindViewHolder, ja, zastosowałem tę koncepcję na przycisku, kiedy klikam przycisk, zmienia się obraz tła przycisku. Przedmiotem przycisku, którego użyłem, są nazwy jako "upvote", ponieważ "i" utrzymuje pozycję każdego elementu w widoku recyklera, użyłem go jako indeksu tablicy, która działa jak flaga i która śledzi stan elementów.

@Override
public void onBindViewHolder(final PersonViewHolder personViewHolder, final int i) {
    if(i==PersonViewHolder.k) {
        personViewHolder.b1.setText("load more");

    }
    else
     {
        personViewHolder.question.setText(persons.get(i).name);
        personViewHolder.personAge.setText(persons.get(i).age);

         if(personViewHolder.check[i]==0)
         {personViewHolder.upvote.setBackgroundResource(R.drawable.noupvote);
         }
         else
         {
             personViewHolder.upvote.setBackgroundResource(R.drawable.upvote);

         }

         personViewHolder.upvote.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 if(personViewHolder.check[i]==0)
                 {personViewHolder.check[i]=1;
                     personViewHolder.upvote.setBackgroundResource(R.drawable.upvote);


                 }
                 else
                 {personViewHolder.check[i]=0;
                     personViewHolder.upvote.setBackgroundResource(R.drawable.noupvote);

                 }


             }
         });
        // personViewHolder.personPhoto.setImageResource(persons.get(i).photoId);
    }

}
Vishal kharb
źródło
1

Miałem ten sam problem. Kiedy klikałem przycisk przełączania elementu w moim okienku recyklingu, zaznaczony przycisk Przełącz pojawiał się w co dziesiątym elemencie (na przykład, jeśli został kliknięty w pozycji z indeksem 0, kliknięto również elementy z indeksami 9, 18, 27). Po pierwsze, mój kod w onBindViewHolder to:

if (newsItems.get(position).getBookmark() == 1) {
            holder.getToggleButtonBookmark().setChecked(true);
        }

Ale potem dodałem stwierdzenie Else

if (newsItems.get(position).getBookmark() == 1) {
            holder.getToggleButtonBookmark().setChecked(true);
//else statement prevents auto toggling
        } else{
            holder.getToggleButtonBookmark().setChecked(false);
        }

I problem został rozwiązany

LA_Homie
źródło
Dziękuję Ci. W innym przypadku część wyczyści pole wyboru, jeśli jest zaznaczone domyślnie podczas recyklingu, aby wyświetlić ten sam widok.
Adarsh ​​Vijayan P
1

okej jest wiele odpowiedzi tutaj zamieszczę swój kod i po prostu wyjaśnię, co zrobiłem ... może to pomoże młodym uczniom takim jak ja: D.

1- Cel:

stworzymy listę, RecyclerViewktóra ma CheckBoxi RadioButtoncoś takiego:

wprowadź opis obrazu tutaj 2- Klasa modelu

public class ModelClass {
private String time;
private boolean checked;
private boolean free;
private boolean paid;

public TherapistScheduleModel(String time, boolean checked, boolean free, boolean paid) {
    this.time = time;
    this.checked = checked;
    this.free = free;
    this.paid = paid;
}

public boolean isFree() {
    return free;
}

public void setFree(boolean free) {
    this.free = free;
}

public boolean isPaid() {
    return paid;
}

public void setPaid(boolean paid) {
    this.paid = paid;
}

public String getTime() {
    return time;
}

public void setTime(String time) {
    this.time = time;
}

public boolean getChecked() {
    return checked;
}

public void setChecked(boolean checked) {
    this.checked= checked;
}
}

3-My Amazing Adapter

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private ListAllListeners listAllListeners;
private ArrayList<ModelClass> mDataList;

public MyAdapter(Context context, ArrayList<ModelClass> mDataList,
                             ListAllListeners listAllListeners) {
    this.mDataList = mDataList;
    this.listAllListeners = listAllListeners;
    this.context = context;
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View view = inflater.inflate(R.layout.single_view, parent, false);
    return new MyViewHolder(view);
}

@Override
public int getItemCount() {
    if (mDataList != null)
        return mDataList.size();
    else
        return 0;
}

@Override
public void onBindViewHolder(@NonNull final MyViewHolder holder, final int position) {
     //important to:
    //setOnCheckedChangeListener to 'null'
    holder.checkBoxTime.setOnCheckedChangeListener(null);
    holder.freeRB.setOnCheckedChangeListener(null);
    holder.paidRB.setOnCheckedChangeListener(null);

    //Check Box
            holder.checkBoxTime.setText(mDataList.get(holder.getAdapterPosition()).getTime());
    //here we check if the item is checked or not from the model.
    if(mDataList.get(holder.getAdapterPosition()).getChecked())
        holder.checkBoxTime.setChecked(true);
    else
        holder.checkBoxTime.setChecked(false);

    holder.checkBoxTime.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
            if (b) {
                mDataList.get(holder.getAdapterPosition()).setChecked(true);
                listAllListeners.onItemCheck(holder.checkBoxTime.getText().toString(), holder.getAdapterPosition());
            }
            else {
                mDataList.get(holder.getAdapterPosition()).setChecked(false);
                listAllListeners.onItemUncheck(holder.checkBoxTime.getText().toString(), holder.getAdapterPosition());
            }
        }
    });

    //Radio Buttons

    if(mDataList.get(holder.getAdapterPosition()).isFree())
        holder.freeRB.setChecked(true);
    else
        holder.freeRB.setChecked(false);
    holder.freeRB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
            if (b) {
                mDataList.get(holder.getAdapterPosition()).setFree(true);
                listAllListeners.onFreeCheck(holder.freeRB.getText().toString(), holder.getAdapterPosition());
            } else {
                mDataList.get(holder.getAdapterPosition()).setFree(false);
                listAllListeners.onFreeUncheck(holder.freeRB.getText().toString(), holder.getAdapterPosition());
            }
        }
    });

   //***and so on to paidRB***

}//end onBindViewHolder()

public interface ListAllListeners {
//here is a list of clicked listeners to use them as you want ;).
//you can get a list of checked or unChecked of all 
        void onItemCheck(String checkBoxName, int position);
        void onItemUncheck(String checkBoxName, int position);
        void onFreeCheck(String name, int pos);
        void onFreeUncheck(String name, int pos);
        void onPaidCheck(String name, int pos);
        void onPaidUncheck(String name, int pos);
    }

    class MyViewHolder extends RecyclerView.ViewHolder {

        CheckBox checkBoxTime;
        RadioButton freeRB, paidRB;

        MyViewHolder(View itemView) {
            super(itemView);
            checkBoxTime = itemView.findViewById(R.id.timeCheckBox);
            freeRB = itemView.findViewById(R.id.freeRadioBtn);
            paidRB = itemView.findViewById(R.id.paidRadioBtn);
        }
    }//end class MyViewHolder

    }//end class

3- W Aktywności otrzymujesz coś takiego:

myAdapter= new MyAdapter(getActivity().getApplicationContext(), mDataList,
                new MyAdapter.ListAllListeners() {

                    @Override
                    public void onItemCheck(String checkBoxName, int position) {
                        Toast.makeText(getActivity(), "" + checkBoxName + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onItemUncheck(String checkBoxName, int position) {
                        Toast.makeText(getActivity(), "" + checkBoxName + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onFreeCheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onFreeUncheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onPaidCheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onPaidUncheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }
                });
Mahmoud Ayman
źródło
0

Miałem ten sam problem na liście RecyclerView z przełącznikami i rozwiązałem go za pomocą odpowiedzi @oguzhand, ale z tym kodem wewnątrz checkChangeListener:

if (buttonView.isPressed()) {
    if (isChecked) {
        group.setSelected(true);
    } else {
        group.setSelected(false);
    }
}else{
    if (isChecked) {
        buttonView.setChecked(false);
    } else {
        buttonView.setChecked(true);
    }
}

(Gdzie „grupa” to podmiot, który chcę zaznaczyć / odznaczyć)

Granjero
źródło
0

public class TagYourDiseaseAdapter extends RecyclerView.Adapter {private ReCyclerViewItemClickListener mRecyclerViewItemClickListener; prywatny kontekst mContext;

List<Datum> deviceList = Collections.emptyList();

/**
 * Initialize the values
 *
 * @param context : context reference
 * @param devices : data
 */

public TagYourDiseaseAdapter(Context context, List<Datum> devices,
                             ReCyclerViewItemClickListener mreCyclerViewItemClickListener) {
    this.mContext = context;
    this.deviceList = devices;
    this.mRecyclerViewItemClickListener = mreCyclerViewItemClickListener;
}


/**
 * @param parent   : parent ViewPgroup
 * @param viewType : viewType
 * @return ViewHolder
 * <p>
 * Inflate the Views
 * Create the each views and Hold for Reuse
 */
@Override
public TagYourDiseaseAdapter.OrderHistoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tag_disease, parent, false);
    TagYourDiseaseAdapter.OrderHistoryViewHolder myViewHolder = new TagYourDiseaseAdapter.OrderHistoryViewHolder(view);
    return myViewHolder;
}


/**
 * @param holder   :view Holder
 * @param position : position of each Row
 *                 set the values to the views
 */
@Override
public void onBindViewHolder(final TagYourDiseaseAdapter.OrderHistoryViewHolder holder, final int position) {
    Picasso.with(mContext).load(deviceList.get(position).getIconUrl()).into(holder.document);
    holder.name.setText(deviceList.get(position).getDiseaseName());

    holder.radioButton.setOnCheckedChangeListener(null);
    holder.radioButton.setChecked(deviceList.get(position).isChecked());

    //if true, your checkbox will be selected, else unselected
    //holder.radioButton.setChecked(objIncome.isSelected());

    holder.radioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            deviceList.get(position).setChecked(isChecked);
        }
    });


}

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


/**
 * Create The view First Time and hold for reuse
 * View Holder for Create and Hold the view for ReUse the views instead of create again
 * Initialize the views
 */

public class OrderHistoryViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    ImageView document;
    TextView name;
    CheckBox radioButton;

    public OrderHistoryViewHolder(View itemView) {
        super(itemView);
        document = itemView.findViewById(R.id.img_tag);
        name = itemView.findViewById(R.id.text_tag_name);
        radioButton = itemView.findViewById(R.id.rdBtn_tag_disease);
        radioButton.setOnClickListener(this);
        //this.setIsRecyclable(false);
    }


    @Override
    public void onClick(View view) {
        mRecyclerViewItemClickListener.onItemClickListener(this.getAdapterPosition(), view);
    }
}

}

velraj
źródło
0

stanie się tak, gdy używasz setOnCheckedChangeListenerzamiast tego użycia setObClickListeneri wewnątrz, po prostu wykonaj ten prosty uchwyt:

   if (list.get(position).isCheck())
            {
                list.get(position).setCheck(false);
            }
            else
            {
                list.get(position).setCheck(true);
            }

UWAGA: w swoim modelu listy dodaj jedną zmienną logiczną o nazwie checki ustaw dla niej getter i setter, w powyższym przypadku mój to setCheck i isCheck

mam nadzieję, że komuś pomoże, jeśli tak + zagłosuj na tę odpowiedź

E-zad
źródło
0

Dodanie setItemViewCacheSize (int size) do recyclinglerview i przekazanie rozmiaru listy rozwiązało mój problem.

mykod:

mrecyclerview.setItemViewCacheSize(mOrderList.size());
mBinding.mrecyclerview.setAdapter(mAdapter);

Źródło: https://stackoverflow.com/a/46951440/10459907

Pravin Yadav
źródło
0

Wynika to z ciągłego tworzenia widoku, najlepszą opcją jest wyczyszczenie pamięci podręcznej przed ustawieniem adaptera

recyclerview.setItemViewCacheSize(your array.size());
Mrutyunjay Swain
źródło
0

Kompletny przykład
klasy publicznej ChildAddressAdapter extends RecyclerView.Adapter <ChildAddressAdapter.CartViewHolder> {

private Activity context;
private List<AddressDetail> addressDetailList;
private int selectedPosition = -1;

public ChildAddressAdapter(Activity context, List<AddressDetail> addressDetailList) {
    this.context = context;
    this.addressDetailList = addressDetailList;
}

@NonNull
@Override
public CartViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

    LayoutInflater inflater = LayoutInflater.from(context);
    View myView = inflater.inflate(R.layout.address_layout, parent, false);
    return new CartViewHolder(myView);
}

@Override
public void onBindViewHolder(@NonNull CartViewHolder holder, int position) {

    holder.adress_checkbox.setOnClickListener(view -> {
        selectedPosition = holder.getAdapterPosition();
        notifyDataSetChanged();
    });

    if (selectedPosition==position){
        holder.adress_checkbox.setChecked(true);
    }
    else {
        holder.adress_checkbox.setChecked(false);
    }


}

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

class CartViewHolder extends RecyclerView.ViewHolder
{
    TextView address_text,address_tag;
    CheckBox adress_checkbox;

    CartViewHolder(View itemView) {
        super(itemView);
        address_text = itemView.findViewById(R.id.address_text);
        address_tag = itemView.findViewById(R.id.address_tag);
        adress_checkbox = itemView.findViewById(R.id.adress_checkbox);
    }
}

}

Mudassar Ashraf
źródło
-1

W moim przypadku zadziałało unieważnienie słuchaczy w obiekcie viewHolder, gdy widok ma zostać przetworzony ( onViewRecycled):

 override fun onViewRecycled(holder: AttendeeViewHolder) {
            super.onViewRecycled(holder)
            holder.itemView.hasArrived.setOnCheckedChangeListener(null);
            holder.itemView.edit.setOnClickListener { null }
        }
shannoga
źródło