'Seekbars in RecyclerView become "linked". Changing the value in one, changes another

I've set up a RecyclerView and getting some odd behavior when adding Seekbars to every row or "view". I've also tried this just using ListView as well and get the same effect. So when I change the Seekbar on the first item, it changes the value of the seekbar on the seventh item down the list to the exact same value. It seems like they are getting "linked" somehow. Currently, the layout_height is smaller than the list, so its scrollable and I cant see all the items at once, and this is when I notice the problem. However, when the layout_height is maxed to match_parent, and the whole list fits on the screen, this does not happen. I'm really rough guessing this has something to do with how views get inflated and its getting ID's confused or something like this. My RecyclerView Adapter code is listed below. This is my first time using StackOverflow, apologies if I didn't post this in the right area or made any mistakes with formatting. Let me know if I could of done something better here.

Thanks for any help!!

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

    private ArrayList<HashMap<String, String>> mData;
    private LayoutInflater mInflater;
    private ItemClickListener mClickListener;
    private LinearLayoutManager linearLayoutManager;

    // data is passed into the constructor
    MyRecyclerViewAdapter(Context context, ArrayList<HashMap<String, String>> data) {
        this.mInflater = LayoutInflater.from(context);
        this.mData = data;
    }

    // inflates the row layout from xml when needed
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.recyclerview_row, parent, false);
        return new ViewHolder(view);
    }

    // binds the data to the TextView in each row
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {

       // System.out.println(mData);
        ArrayList<HashMap<String, String>> test = mData;
        //entry.get("id").toString()
        holder.Foods_Text.setText(mData.get(position).get("Food"));
        holder.Prices_Text.setText(mData.get(position).get("Price"));
        holder.Weights_Text.setText(mData.get(position).get("Weight"));


        holder.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            public void onStopTrackingTouch(SeekBar bar) {

            }
            public void onStartTrackingTouch(SeekBar bar) {
            }
            public void onProgressChanged(SeekBar bar, int paramInt, boolean paramBoolean) {

                int value = bar.getProgress();
                // stList.get(position).setNoOfquestions(value);

                System.out.println(value);

                //update textview while dragging seekbar
                //  viewHolder.tvValues.setText("" + paramInt); // here in textView the percent will be shown
            }
        });

        
    }

    // total number of rows
    @Override
    public int getItemCount() {
        return mData.size();
    }


    // stores and recycles views as they are scrolled off screen
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        TextView Foods_Text;
        TextView Prices_Text;
        TextView Weights_Text;
        SeekBar seekBar;

        ViewHolder(View itemView) {
            super(itemView);
            Foods_Text = itemView.findViewById(R.id.Foods);
            Prices_Text = itemView.findViewById(R.id.Prices);
            Weights_Text = itemView.findViewById(R.id.Weights);
            seekBar = itemView.findViewById(R.id.seekBar_Food);
            seekBar.setMax(20);
            itemView.setOnClickListener(this);
           
        }

        @Override
        public void onClick(View view) {
            if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
        }


    }

    // convenience method for getting data at click position
    String getItem(int id) {
        return mData.get(id).toString();
    }

    // allows clicks events to be caught
    void setClickListener(ItemClickListener itemClickListener) {
        this.mClickListener = itemClickListener;
    }

    // parent activity will implement this method to respond to click events
    public interface ItemClickListener {
        void onItemClick(View view, int position);
    }
}


Solution 1:[1]

Your onBindViewHolder() method doesn't have anything to set the value (the progress) of the SeekBar. This means that, when the view is recycled, the SeekBar will just re-use the position from the previous item it was bound to.

Imagine that you can fit five rows on screen at once. All SeekBars are initially set to 0. You drag the first one to 100.

Now you scroll the list. Eventually, the first row will scroll out of view, and its ViewHolder will be recycled for some row that comes into view (maybe the seventh or eighth row; some "extra" ViewHolders are cached). When this row is recycled, since nothing sets the SeekBar position, it will still display as 100.

You will have to update onBindViewHolder() to make sure that the SeekBar's progress is set correctly every time.

Solution 2:[2]

Add the override method

 @Override
 public int getItemViewType(int position) {
     return position;
 } 

in the Adapter class

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ben P.
Solution 2 Maclaine