java RecyclerView 滚动时弄乱了数据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30082000/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 16:25:54  来源:igfitidea点击:

RecyclerView messed up data when scrolling

javaandroidandroid-recyclerview

提问by k.nadonenko

Having a problem when scrolling RecyclerView after scrolling down and up. The idea is to change elements color, but when I scroll down everything is great and when the scroll goes up - the elements, which are shouldn't be colored are changing color.

向下和向上滚动后滚动 RecyclerView 时出现问题。这个想法是改变元素的颜色,但是当我向下滚动时,一切都很好,当滚动向上时 - 不应该被着色的元素正在改变颜色。

Here's my adapter:

这是我的适配器:

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

private NotificationData notificationData;
private Context mContext;
private ArrayList<NotificationData> infromationList = new ArrayList<>();


public NotificationsAdapter(Context context, ArrayList<NotificationData> infromationList) {
    this.infromationList = infromationList;
    this.mContext = context;
}


@Override
public NotificationsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View itemLayoutView;
    ViewHolder viewHolder;

    itemLayoutView = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.notification_single_item, parent, false);
    viewHolder = new ViewHolder(itemLayoutView, viewType);

    return viewHolder;
}

@Override
public void onBindViewHolder(NotificationsAdapter.ViewHolder holder, int position) {

    notificationData = infromationList.get(position);
    holder.notificationDate.setText(convertDate(notificationData.getDate()));
    holder.notificationStatus.setText(notificationData.getNotificationStatus());
    holder.orderDescription.setText(notificationData.getNotificationLabel());

    if ("true".equals(notificationData.getReadStatus())) {
        holder.root.setBackgroundColor(mContext.getResources().getColor(R.color.white));
        holder.notificationStatus.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
    }

}

@Override
public int getItemCount() {
    return (null != infromationList ? infromationList.size() : 0);
}

public static class ViewHolder extends RecyclerView.ViewHolder {

    public TextView notificationDate;
    public TextView notificationStatus;
    public TextView orderDescription;
    public LinearLayout root;

    public ViewHolder(View itemView, int position) {
        super(itemView);

        notificationDate = (TextView) itemView.findViewById(R.id.notificationDate);
        notificationStatus = (TextView) itemView.findViewById(R.id.notificationStatus);
        orderDescription = (TextView) itemView.findViewById(R.id.orderDescription);
        root = (LinearLayout) itemView.findViewById(R.id.root);
    }

}

private String convertDate(String date) {
    String convertedDate;

    String[] parts = new String[2];
    parts = date.split("T");
    date = parts[0];

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
    Date testDate = null;
    try {
        testDate = sdf.parse(date);
    }catch(Exception ex){
        ex.printStackTrace();
    }
    SimpleDateFormat formatter = new SimpleDateFormat("dd.mm.yyyy");
    convertedDate = formatter.format(testDate);

    return convertedDate;
}
}

回答by Jhonatan Sabadi

I had the same problem and the only solution I found for this is:

我遇到了同样的问题,我找到的唯一解决方案是:

holder.setIsRecyclable(false);

Your recycler will not recycle anymore so the items will be the same when you scroll, and if you want to delete some item do not use notifyitemRemoved(position), use notifyDataSetChanged()instead.

您的回收站将不再回收,因此滚动时项目将相同,如果您想删除某些项目,请不要使用notifyitemRemoved(position)notifyDataSetChanged()而是使用。

回答by Nathan Teyou

Add setHasStableIds(true);in your adapter constructor and Override these two methodes in adapter.

添加setHasStableIds(true);您的适配器构造函数并在适配器中覆盖这两个方法。

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

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

回答by Xcihnegn

There is problem in your onBindViewHolder(...), should be:

你的有问题onBindViewHolder(...),应该是:

if ("true".equals(notificationData.getReadStatus())) {
    holder.root.setBackgroundColor(mContext.getResources().getColor(R.color.white));
    holder.notificationStatus.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
}
else {
    holder.root.setBackgroundColor(yourDefaultColor);
    holder.notificationStatus.setTypeface(yourDefaultTypeface);

}

回答by efr

Try adding this in the adapter.

尝试在适配器中添加它。

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

回答by Sachin Mandhare

onBindHolder called several times as Recycler View needs a view unless new one. So each time you set visilibity in child views, other views states are also changes.

onBindHolder 多次调用,因为 Recycler View 需要一个视图,除非是新视图。所以每次你在子视图中设置可见性时,其他视图状态也会发生变化。

Whenever you scroll up and down, these views are getting re-drawed with wrong visibility options so always specify both the conditions cause recycler view doesn't know the previous state/conditions/values of our widgets.

每当您上下滚动时,这些视图都会使用错误的可见性选项重新绘制,因此请始终指定这两个条件,因为回收器视图不知道我们小部件的先前状态/条件/值。

Solution :

解决方案 :

If in If block you set visibility of any android widget.setVisibility(View.Gone) then in else block you have to set it's visibility opposite value like widget.setVisibility(View.Visible) to overcome the above problem.

如果在 If 块中您设置了任何 android widget.setVisibility(View.Gone) 的可见性,那么在 else 块中您必须设置它的可见性相反的值,如 widget.setVisibility(View.Visible) 以克服上述问题。

 @Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {

    viewHolder.tvName.setText(ModelCategoryProducts.name.get(i));
    viewHolder.tvPrice.setText("Rs."+String.format("%.2f", Float.parseFloat(ModelCategoryProducts.price.get(i))));
    if(ModelCategoryProducts.special_price.get(i).equals("null")) {
        viewHolder.tvSpecialPrice.setVisibility(View.GONE); // here visibility is gone and in else it's opposite visibility i set.
        viewHolder.tvPrice.setTextColor(Color.parseColor("#ff0000"));
        viewHolder.tvPrice.setPaintFlags(0);// here paint flag is 0 and in else it's opposite flag that i want is set.
    }else if(!ModelCategoryProducts.special_price.get(i).equals("null")){
        viewHolder.tvPrice.setTextColor(Color.parseColor("#E0E0E0"));
        viewHolder.tvSpecialPrice.setVisibility(View.VISIBLE);
        viewHolder.tvSpecialPrice.setText("Rs." + String.format("%.2f", Float.parseFloat(ModelCategoryProducts.special_price.get(i))));
        viewHolder.tvPrice.setPaintFlags(viewHolder.tvPrice.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
    }
    if (!ModelCategoryProducts.image_url.get(i).isEmpty()) {
        Picasso.with(context)
                .load(ModelCategoryProducts.image_url.get(i))
                .into(viewHolder.ivProduct);
    }

    viewHolder.setClickListener(new ItemClickListener() {
        @Override
        public void onClick(View view, int position, boolean isLongClick) {
            if (isLongClick) {
//                    Toast.makeText(context, "#" + position + " - " + ModelCategoryProducts.name.get(position) + " (Long click)", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, "#" + position + " - " + ModelCategoryProducts.name.get(position), Toast.LENGTH_SHORT).show();
                Intent i = new Intent(context, ProductDetail.class);
                i.putExtra("position",position);
                i.putExtra("flagHlvCheck", 5);
                context.startActivity(i);
            }
        }
    });
}

回答by Mohammad nabil

 @Override
public DataObjectHolder onCreateViewHolder(ViewGroup parent,
                                           int viewType) {
    View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.custom_layout, parent, false);

    DataObjectHolder dataObjectHolder = new DataObjectHolder(view);
    dataObjectHolder.setIsRecyclable(false);

    return dataObjectHolder;
}

回答by Keshav Gera

@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
    final UserData userdata = userdataList.get(position);

    holder.setIsRecyclable(false);

    holder.name.setText(userdata.getName());
    holder.active.setChecked(userdata.getActive());

    String userPic = userdata.getPic();


    holder.active.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked){
            userdata.setActive(isChecked);
        }
    });
}

回答by Jetwiz

class AnyRVAdapter: androidx.recyclerview.widget.RecyclerView.Adapter<AnyRVAdapter.MViewHolder>() {

// put saver outside viewholder
val saveLayId = mutableListOf<Int>()

inner class MViewHolder(itemView: View) :
    androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) {       

    fun bindModel(d: TesListModel.MList, position:Int) {

       // concept here
       val showedId= saveLayId.find { s -> s == layoutPosition}
       if (idClicked == null) {            
           // save the layout id
           lyClicked.visibility = View.VISIBLE
           saveLayId.add(layoutPosition)
       } else {
           // remove the layout id
           lyClicked.visibility = View.INVISIBLE
           saveLayId.remove(layoutPosition)
       }         
    }
}

but i think this code is heavy if you use for large data set.

但我认为如果您用于大型数据集,这段代码很重。

回答by kapsid

If someone might face issues with some of the fields in the viewholder getting random values, then try to set all the fields with atleast any default value.

如果有人可能会遇到查看器中某些字段获取随机值的问题,请尝试将所有字段设置为至少任何默认值。

回答by Mohammad Davari

The best way is indicate an ArrayList for example as a Model and have some parameters and define setter and getter for that.

最好的方法是将 ArrayList 表示为模型,并具有一些参数并为此定义 setter 和 getter。

package com.test.mohammaddvi.snappfood.Model;

public class OfferList {
private boolean visibilityOrder;
private int number;

public OfferList(int number, boolean visibilityOrder) {
   this.number=number;
   this.visibilityOrder=visibilityOrder;
}

public boolean isVisibilityOrder() {
    return visibilityOrder;
}

public void setVisibilityOrder(boolean visibilityOrder) {
    this.visibilityOrder = visibilityOrder;
}

public int getNumber() {
    return number;
}

public void setNumber(int number) {
    this.number = number;
}

}

}

and set the the variables as where you want and for get you must do it in onBindViewHolder of your recyclerview Adapter:

并将变量设置为您想要的位置,并且您必须在 recyclerview 适配器的 onBindViewHolder 中执行此操作:

if (offerList.isVisibilityOrder()) {
        holder.foodMinusButton.setVisibility(View.VISIBLE);
        holder.foodOrderNumber.setText(offerList.getNumber() + "");
        holder.foodOrderNumber.setVisibility(View.VISIBLE);
    } else {
        holder.foodMinusButton.setVisibility(View.INVISIBLE);
    }

and indicate it your recyclerview adapter:

并指明您的 recyclerview 适配器:

public class RecyclerViewMenuFragmentAdapter extends RecyclerView.Adapter<RecyclerViewMenuFragmentAdapter.SingleItemInMenuFragment> {

private ArrayList<Food> foodList;
private Context mContext;
private List<OfferList> offers;

public RecyclerViewMenuFragmentAdapter(ArrayList<Food> foodList, Context mContext, List<OfferList> offers) {
    this.foodList = foodList;
    this.mContext = mContext;
    this.offers = offers;
}