CheckBox no RecyclerView continua verificando itens diferentes

94

Aqui está o XML para meus itens dentro do 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>

E aqui está o adaptador RecyclerView que aumenta o layout acima para cada um de seus itens:

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));
    }
}

O problema é, digamos que eu tenha 10 itens dentro do RecyclerView. Quando eu marquei a caixa de seleção no item 1,2,3, percorri o RecyclerView e, de repente, alguns dos outros itens, por exemplo, itens 8,9, estão marcados. E quando eu rolar para cima novamente, os itens 1 e 3 são verificados, mas não o item 2. Alguma idéia de por que isso aconteceu?

estou dentro
fonte
Tente usar esta biblioteca , consulte ViewStates. Ajuda a salvar um estado ao rolar.
Vitaly

Respostas:

167

Esse é um comportamento esperado. Você não está definindo sua caixa de seleção marcada ou não. Você está selecionando um e o titular da visualização o mantém selecionado. Você pode adicionar uma variável booleana em seu objeto ObjectIncome e manter o status de seleção do seu item.

Você pode olhar meu exemplo. Você pode fazer algo assim:

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
fonte
20
Não funcionou. Você tem que escrever holder.cbSelect.setOnCheckedChangeListener(null);antesholder.cbSelect.setChecked(objIncome.isSelected())
Jemshit Iskenderov
2
há algum motivo para definir holder.cbSelect.setOnCheckedChangeListener (null); trabalho?
Deb
4
@oguzhand oi Tentei sua solução, mas não está funcionando de qualquer maneira: com ou sem definir o ouvinte como nulo.
Abbas
3
@oguzhand Aqui está o código de 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 você precisa manter o status da caixa de seleção em um objeto, porque ViewHolder é apenas um titular. Se você tiver 100 itens, terá apenas cerca de 6-7 (depende do tamanho da tela e do layout) ViewHolders e usará todos esses objetos em um ciclo.
Oğuzhan Döngül
22

Resumindo, é porque reciclamos as visualizações e as usamos novamente!

como você pode evitar isso:

1. Em onBindViewHolderverificar se você deve marcar ou desmarcar as caixas. não se esqueça de colocar if e else

if (...)
    holder.cbSelect.setChecked(true);
else
    holder.cbSelect.setChecked(false);
  1. Coloque um ouvinte para a caixa de seleção! sempre que suas estátuas marcadas forem alteradas, atualize o objeto correspondente também em seu myItemsarray! portanto, sempre que uma nova vista é mostrada, ele lê a estátua mais recente do objeto.
Omid Heshmatinia
fonte
Seu segundo ponto foi a chave. Embora funcione melhor em uma situação, quando o conjunto de dados inicial também tem informações sobre o estado verificado (o que é o meu caso)
Átila Orosz
1
esta é a resposta mais direta e correta. SetCheck para true e false em onBindViewHolder é a chave
Beeing Jk
No meu caso, eu tenho que salvar os dados no modelo de dados com o valor padrão isChecked falsepara todos os dados definidos no início, então onCheckChangedeu apenas atualizei o isCheckedvalor para trueou falsee conforme informado no implemento de resposta que verifica está marcada ou não.
Ali Tamoor
20

USE ISSO SOMENTE SE VOCÊ TIVER NÚMERO LIMITADO DE ITENS EM SUA VISUALIZAÇÃO DO RECICLADOR.
Tentei usar valor booleano no modelo e manter o status da caixa de seleção, mas não adiantou no meu caso. O que funcionou para mim é this.setIsRecyclable (false);

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

Mais explicações sobre isso podem ser encontradas aqui https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#isRecyclable ()

NOTA: Esta é uma solução alternativa. Para usá-lo corretamente, você pode consultar o documento que afirma "As chamadas para setIsRecyclable () devem sempre ser emparelhadas (uma chamada para setIsRecyclabe (false) deve sempre ser combinada com uma chamada posterior para setIsRecyclable (true)). Pares de chamadas podem ser aninhados , já que o estado é contado por referência internamente. " Não sei como fazer isso no código, se alguém puder fornecer mais código sobre isso.

Rana Ranvijay Singh
fonte
por favor você pode explicar como usá-lo?
UserName_Untold
44
não é um desperdício da lógica por trás do recyclerView?
Eren
3
Tentei fazer isso com uma lista longa, o que resolveu o problema de verificação aleatória, mas quando rolar para baixo e rolar novamente para cima com lista longa, as caixas de seleção marcadas desaparecem :(
SonDang
2
Não é uma boa ideia tornar a visualização não reciclável, pois isso drenará a memória e você perderá muitos dos benefícios da visualização do reciclador.
Arthur
Eu concordo com vocês, @ eren130 e Arthur. Eu editei a postagem e gostaria muito se pudéssemos encontrar uma maneira de usar setIsRecyclable (true / false); devidamente.
Rana Ranvijay Singh
16

Basta adicionar dois métodos de substituição de RecyclerView

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

@Override
public int getItemViewType(int position) {
    return position;
}
Harish Reddy
fonte
2
Não faça isso !! Ele contornará o mecanismo de reciclagem do recyclerView e perderá todo o sentido de usá-lo.
Hanoch Moreno
1
Não, não vai, apenas retorna a posição exata de cada visualização reciclada no suporte de visualização.
Harish Reddy
1
Harish, talvez eu esteja faltando alguma coisa, mas pelo que eu sei, ao fazer isso você realmente diz ao adaptador que o número de tipos de itens é a contagem de itens. O significado é que nenhum item pode ser reciclado porque não tem visão semelhante. No entanto, é fácil testar. basta registrar a referência viewHolder.itemView dentro de onBindViewHolder e ver se há dois viewHolders mantendo a mesma referência de exibição. O teste deve ser em uma longa lista para que o sistema de reciclagem seja executado.
Hanoch Moreno
3
Acordou perfeitamente, salvou meu dia.
Kundan
5
caso você tenha mais de 100 itens em sua visualização de reciclagem, esta solução carregará todos os itens de uma vez, isso pode causar OutOfMemoryException caso você tenha imagens. Caso contrário, esta solução é perfeita @Kundan
Harish Reddy
11

Você pode usar a classe Model para controlar a caixa de seleção de cada item recyclerView. A referência completa é de: RecyclerView Checkbox Android

setTag e getTag são usados ​​para controlar o status da caixa de seleção. Verifique o link de referência completo para obter mais informações. Também ensina como enviar itens verificados para NEXTACTIVITY .

Fazer modelo

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;
    }
}

criar integer.xml

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

Finalmente o adaptador se parece com isto:

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

Usando Kotlin, a única coisa que resolveu esse problema para mim foi limpar o OnCheckedChangeListenerantes de definir a variável e, em seguida, criar um novo OnCheckedChangeListenerdepoischecked ser definido.

Eu faço o seguinte no meu RecyclerView.ViewHolder

task.setOnCheckedChangeListener(null)
task.isChecked = item.status
task.setOnCheckedChangeListener { _: CompoundButton, checked: Boolean ->
    item.status = checked
    ...
    do more stuff
    ...
}
just_user
fonte
1
Isso está funcionando perfeitamente. Não sei por que, mas isso só funciona quando alguém usando KOTLIN!
Aditya S.
Isso está realmente funcionando. O que ele fez é muito inteligente e óbvio ao mesmo tempo. task.setOnCheckedChangeListener(null)apenas consideramos a caixa de seleção como nova, pois estamos interessados ​​apenas em novos eventos. Novamente muito inteligente.
AbdElraouf Sabri
2

Eu recomendo que não use checkBox.setOnCheckedChangeListenerem recyclerViewAdapter. Porque no scrolling recyclerView, checkBox.setOnCheckedChangeListenerserá disparado pelo adaptador. Não é seguro . Em vez disso, use checkBox.setOnClickListenerpara interagir com as entradas do usuário.

Por exemplo:

     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
fonte
2

Conforme declarado acima, o estado verificado do objeto deve ser incluído nas propriedades do objeto. Em alguns casos, você também pode precisar alterar o estado de seleção do objeto clicando no próprio objeto e deixar o CheckBox informar sobre o estado atual (selecionado ou não). A caixa de seleção usará então o estado do objeto na posição real do adaptador fornecido que é (por padrão / na maioria dos casos) a posição do elemento na lista.

Verifique o snippet abaixo, pode ser útil.

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
fonte
2

No meu caso, funcionou.

@Override
public void onViewRecycled(MyViewHolder holder) {
    holder.checkbox.setChecked(false); // - this line do the trick
    super.onViewRecycled(holder);
}
tollestheanimal
fonte
2

Use uma matriz para manter o estado dos itens

No adaptador, use um Mapa ou um SparseBooleanArray (que é semelhante a um mapa, mas é um par de valores-chave int e booleano) para armazenar o estado de todos os itens em nossa lista de itens e, em seguida, use as chaves e valores para comparar ao alternar o estado verificado

No adaptador, crie um SparseBooleanArray

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

    private SparseBooleanArray itemStateArray= new SparseBooleanArray();

em seguida, no manipulador de clique de item, onClick()use o estado dos itens no itemStateArray para verificar antes de alternar, aqui está um exemplo

        @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);
            }
        }

Além disso, use uma matriz booleana esparsa para definir o estado verificado quando a visualização estiver ligada

@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);
        }
    }

e adaptador final será como este

Basi
fonte
1

Tive o mesmo problema em uma lista de RecyclerView com switches, e resolvi usando @oguzhand answer, mas com este código dentro do CHECKChangeListener:

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

(Onde 'grupo' é a entidade que desejo selecionar / desmarcar)

Granjero
fonte
1

Você precisa separar as interações onBindViewHolder (lógica) com CheckBox e as interações do usuário com checkbox. Usei OnCheckedChangeListener para interações do usuário (obviamente) e ViewHolder.bind () para lógica, é por isso que você precisa definir o listener verificado como nulo antes de configurar o holder e depois que o holder estiver pronto - configure o listener verificado para interações do usuário.

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
fonte
1

O problema desta solução que encontrei é, ao criar um array global estático e usá-lo no ADAPER CLASS "onBindViewHolder", no qual criei todos os objetos / objetos globais necessários.

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);

        }

    }

}

Aqui (NO CONSTRUTOR de RVADAPTER CLASS), dei o tamanho ao array igual ao tamanho / número de itens que irei exibir na visualização do reciclador

List<Person> persons;

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

BindViewHolder, I, Apliquei este conceito em um botão, quando eu clico em um botão, a imagem de fundo do botão muda. O objeto do botão que usei é nomes como "upvote", já que "i" mantém a posição de cada item na visualização do reciclador, usei-o como um índice de array que está funcionando como uma bandeira e que está acompanhando o status dos elementos.

@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
fonte
1

Eu tive o mesmo problema. Quando eu estava clicando no botão de alternância do item em meu recyclerView, o botão de alternância marcado aparecia a cada 10 itens (por exemplo, se ele foi clicado em um item com índice 0, itens com 9, 18, 27 índices estavam sendo clicados também). Em primeiro lugar, meu código em onBindViewHolder era:

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

Mas então eu adicionei outra declaração

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

E o problema foi resolvido

LA_Homie
fonte
Obrigado. Caso contrário, a caixa de seleção será desmarcada se ela for marcada por padrão ao reciclar a mesma visualização.
Adarsh ​​Vijayan P
1

ok, há muitas respostas aqui , postarei meu código e simplesmente explicarei o que fiz ... talvez ajude juniores como eu: D.

1- Objetivo:

vamos criar uma lista de RecyclerViewque tem CheckBoxe RadioButton, algo assim:

insira a descrição da imagem aqui 2- Classe de modelo

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- Na atividade, você obtém algo assim:

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
fonte
0

public class TagYourDiseaseAdapter extends RecyclerView.Adapter {private ReCyclerViewItemClickListener mRecyclerViewItemClickListener; contexto privado 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
fonte
0

isso acontecerá quando usar em setOnCheckedChangeListenervez desse uso setObClickListenere dentro de fazer este manuseio fácil:

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

NOTA: em seu modelo de lista, adicione uma variável booleana com nome checke defina getter e setter para isso, no caso acima o meu é setCheck e isCheck

espero que ajude alguém em caso afirmativo + vote nesta resposta

E-zad
fonte
0

Adicionar setItemViewCacheSize (int size) para recyclerview e passar o tamanho da lista resolveu meu problema.

mycode:

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

Fonte: https://stackoverflow.com/a/46951440/10459907

Pravin Yadav
fonte
0

isso se deve à criação repetida de visualização, a melhor opção é limpar o cache antes de configurar o adaptador

recyclerview.setItemViewCacheSize(your array.size());
Mrutyunjay Swain
fonte
0

Exemplo completo de
classe pública 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
fonte
-1

O que funcionou para mim é anular os ouvintes no viewHolder quando a visualização vai ser reciclada ( onViewRecycled):

 override fun onViewRecycled(holder: AttendeeViewHolder) {
            super.onViewRecycled(holder)
            holder.itemView.hasArrived.setOnCheckedChangeListener(null);
            holder.itemView.edit.setOnClickListener { null }
        }
Shannoga
fonte