Gabriel February 2016

RecyclerView causes issue when recycling

I have a list of item that I created using RecyclerView. When the user clicks on one of them i change the background color of that selected item. The problem is, when I scroll through my items, and they get recycled, some of the items get the selected item's background color (which is wrong). Here you can see my Adapter's code:

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

private static final String SELECTED_COLOR = "#ffedcc";

private List<OrderModel> mOrders;

public OrderAdapter() {
    this.mOrders = new ArrayList<>();
}

public void setOrders(List<OrderModel> orders) {
    mOrders = orders;
}

public void addOrders(List<OrderModel> orders) {
    mOrders.addAll(0, orders);
}

public void addOrder(OrderModel order) {
    mOrders.add(0, order);
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    Context context = parent.getContext();
    LayoutInflater inflater = LayoutInflater.from(context);

    // Inflate the custom layout
    View contactView = inflater.inflate(R.layout.order_main_item, parent, false);

    // Return a new holder instance
    ViewHolder viewHolder = new ViewHolder(contactView);
    return viewHolder;
}

@Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
    final OrderModel orderModel = mOrders.get(position);

    // Set item views based on the data model
    TextView customerName = viewHolder.customerNameText;

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM/dd/yyyy'   'HH:mm:ss:S");
    String time = simpleDateFormat.format(orderModel.getOrderTime());
    customerName.setText(time);

    TextView orderNumber = viewHolder.orderNumberText;
    orderNumber.setText("Order No: " + orderModel.getOrderNumber());

    Button button = viewHolder.acceptButton;
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          

Answers


Gabriele Mariotti February 2016

You should modify your logic assign the value inside the item (object) not the view:

orderItem.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
           orderItem.setSelected(xxxx);
        }
    });

Then in your onBindViewHolder method you have to assing the color according to this value in the item.

if (orderItem.isSelected()){
   viewHolder.orderItem.setBackgroundColor(xxxx);
} else {
  viewHolder.orderItem.setBackgroundColor(xxxx);
}


Dr. Pelocho February 2016

This is quite a common mistake that has an easy solution.

Quick answer: add this line in your onBindViewHolder method:

if (orderItem.isSelected()){
    viewHolder.orderItem.setBackgroundColor(Color.parseColor(SELECTED_COLOR));
} else {
    viewHolder.orderItem.setBackgroundColor(Color.parseColor(DEFAULT_COLOR));
}

(with DEFAULT_COLOR the color that the viewholder has by default)

Explained answer: when the system recycles a viewholder it just calls onBindViewHolder method so if you have changed anything of that viewholder you'll have to reset it. This will happen if you change background, item's position, etc. Any change that is not related to content per se should be reset in that method


Mahendra Chhimwal February 2016

You know what is causing the problem .Right ? Its recyclerView's recycling behavior which assign your out of screen ViewHolder items to new items coming to be displayed on screen. All above answer might solve your problem but I would not suggest you to bind your logic based on ViewHolder object as in all above answers. It will really cause you problem,for sure. From my point of View you should try to build logic based on the state of your data object not ViewHolder Object as you will never know when it gets recycled, as usually.

Suppose you save a satate boolean isSelected in ViewHolder to check, but and if it is true, then the same state will be there for new Item when this viewHolder will be recycled. Hope you get my concern.

Better way to do above is holding the any state in DataModel object. In your case just a boolean isSelected.

sample example like;;

package chhimwal.mahendra.multipleviewrecyclerproject;

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

import java.util.List;

/**
 * Created by mahendra.chhimwal on 12/10/2015.
 */
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> {

    private Context mContext;
    private List<DataModel> mRViewDataList;


    public MyRecyclerViewAdapter(Context context, List<DataModel> rViewDataList) {
        this.mContext = context;
        this.mRViewDataList = rViewDataList;
    }

    @Override
    public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.item_recycler_view, parent, false);
        return new ViewHolder(v 

Post Status

Asked in February 2016
Viewed 1,290 times
Voted 12
Answered 3 times

Search




Leave an answer