All values in RecycleView and ListView are changing while I'm using dialog

Viewed 100

I have a dialog and I want to add a recyclerView or listView and I know that I have to use a List to control items in each row. Now when I want to change a value of a checkbox or a radioButton all the data in List are changing. I used Log and I got that all the booleans data changed at the same time. I moved all items to an activity but the problem still remains. It seems there is a link between the arrayList data.

Also I changed my codes and used SwitchCompat but all Boolean data in mDataSet are changing when I want to change just one of them.

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

    private Context mContext;
    private ArrayList<StructSetOnOffTime> mDataSet;
    private View mView;

    public AdapterSetOnOffTime(Context context, ArrayList<StructSetOnOffTime> dataSet) {
        mContext = context;
        mDataSet = dataSet;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        int id = R.layout.layout_list_settime_items;
        mView = LayoutInflater.from(mContext).inflate(id, parent, false);
        return new ViewHolder(mView);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        holder.mSwitch.setChecked(mDataSet.get(position).setOnOffValue);
        holder.mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
                if(isChecked){
                    mDataSet.get(position).setOnOffValue = true;
                }else{
                    mDataSet.get(position).setOnOffValue = false;
                }
            }
        });
    }

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

    static class ViewHolder extends RecyclerView.ViewHolder {

        public TextView mTextTitle;
        public TextView mTextValue;
        public ImageButton mBtn;
        public SwitchCompat mSwitch;

        public ViewHolder(View itemView) {
            super(itemView);
            mTextTitle = (TextView) itemView.findViewById(R.id.txtSetTime);
            mTextValue = (TextView) itemView.findViewById(R.id.txtOnOffStatus);
            mBtn = (ImageButton) itemView.findViewById(R.id.btnSetTime);
            mSwitch = (SwitchCompat) itemView.findViewById(R.id.swPrefSwitch);
        }
    }
}

Update answer

After wasting a day I recognized that I made a little mistake to fill ArrayList and I'm going to share it.

Previous method of fill adapter:

    StructSetOnOffTime item = new StructSetOnOffTime();
    ArrayList<StructSetOnOffTime> arrayList = new ArrayList<>();
    for(int i = 0; i < 9; i++){
        item.setTimeValue = null;
        item.setOnOffValue = false;
        arrayList.add(item);
    }

As @Anatoli said, I had a problem in this section and I solved it by changing the upper method to this one:

    ArrayList<StructSetOnOffTime> arrayList = new ArrayList<>();
    for(int i = 0; i < 9; i++){
        StructSetOnOffTime item = new StructSetOnOffTime();
        item.setTimeValue = null;
        item.setOnOffValue = false;
        arrayList.add(item);
    }
2 Answers
  1. You set to radOn and radOff same OnCheckedChangeListener and checks in listener if it was set to true. Why you don't make 2 listeners?

    CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                mDataSet.get(position).onOffStatus = true;
    
                // update adapter
                notifyDataSetChanged();
            }
        }
    };
    holder.radOn.setOnCheckedChangeListener(listener);
    
    CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                mDataSet.get(position).onOffStatus = false;
    
                // update adapter
                notifyDataSetChanged();
            }
        }
    };
    holder.radOff.setOnCheckedChangeListener(listener);
    

looks much simpler

  1. calling of

    holder.radOn.setChecked(holder.radOn == buttonView);
    holder.radOff.setChecked(holder.radOff == buttonView);
    

triggers the listeners. To update values in list, just call adapter.notifyDataSetChanged();

UPDATE

For each new item in list you must initialize new Holder

RecyclerView

@Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
    // Inflate the custom layout
    View itemView = LayoutInflater.from(parent.getContext())
            .inflate(layoutId, parent, false);

    return new Holder(itemView, viewType);
}

ArayList

@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
    Holder viewHolder;

    if (convertView == null) {
        convertView = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);

        viewHolder = new Holder(convertView);

        convertView.setTag(viewHolder);
    } else {
        viewHolder = (Holder) convertView.getTag();
    }

    Item item = getItem(position);

    fill(item, position);

    return convertView;
}

When ever you set the check state of your view(from your code also if view didn't get destroyed) your check listener will get called so you need to manually maintain the state. It will work for all radiobutton, checkbox etc.

holder.mSwitch.setOnCheckedChangeListener(null);
holder.mSwitch.setChecked(mDataSet.get(position).setOnOffValue);
holder.mSwitch.setOnClickListener(new OnClickListener(){
    //holder.mSwitch.setCheck(!mDataSet.get(position).setOnOffValue);
    mDataSet.get(position).setOnOffValue = !mDataSet.get(position).setOnOffValue;
});
Related