gpt4 book ai didi

android - RecyclerView 在回收时导致问题

转载 作者:塔克拉玛干 更新时间:2023-11-02 07:59:45 29 4
gpt4 key购买 nike

我有一个使用 RecyclerView 创建的项目列表。当用户单击其中一个时,我会更改该选定项目的背景颜色。问题是,当我滚动浏览我的项目并且它们被回收时,一些项目获得了所选项目的背景颜色(这是错误的)。在这里你可以看到我的 Adapter 的代码:

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) {
viewHolder.userActions.acceptButtonClicked(position);
}
});

final LinearLayout orderItem = viewHolder.orderItem;
orderItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewHolder.userActions.itemClicked(orderModel);
viewHolder.orderItem.setBackgroundColor(Color.parseColor(SELECTED_COLOR));
}
});
}

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


public static class ViewHolder extends RecyclerView.ViewHolder implements OrderContract.View {

public TextView customerNameText;
public Button acceptButton;
public TextView orderNumberText;
public OrderContract.UserActions userActions;
public LinearLayout orderItem;

public ViewHolder(View itemView) {
super(itemView);

userActions = new OrderPresenter(this);

customerNameText = (TextView) itemView.findViewById(R.id.customer_name);
acceptButton = (Button) itemView.findViewById(R.id.accept_button);
orderNumberText = (TextView) itemView.findViewById(R.id.order_number);
orderItem = (LinearLayout) itemView.findViewById(R.id.order_item_selection);
}

@Override
public void removeItem() {

}
}

最佳答案

问题是 recyclerView 回收行为,它将屏幕外的 ViewHolder 项目分配给即将显示在屏幕上的新项目。我不建议您像上述所有答案那样基于 ViewHolder 对象绑定(bind)您的逻辑。它真的会给你带来麻烦。您应该根据数据对象的状态构建逻辑,而不是 ViewHolder 对象,因为您永远不知道它何时被回收。

假设你保存了一个在 ViewHolder 中状态 boolean isSelected 进行检查,但是如果它是真的,那么当这个 viewHolder 将被回收。

更好的方法是在 DataModel 对象中保存任何状态。在您的例子中,只有一个 boolean isSelected。

示例如

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

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bindDataWithViewHolder(mRViewDataList.get(position));
}

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


public class ViewHolder extends RecyclerView.ViewHolder {
private TextView textView;
private LinearLayout llView;
private DataModel mDataItem=null;

public ViewHolder(View itemView) {
super(itemView);
llView=(LinearLayout)itemView.findViewById(R.id.ll_root_view);
textView = (TextView) itemView.findViewById(R.id.tvItemName);
cvItemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// One should handle onclick of event here based on the dataItem i.e. mDataItem in this case.
// something like that..
/* Intent intent = new Intent(mContext,ResultActivity.class);
intent.putExtra("MY_DATA",mDataItem); //If you want to pass data.
intent.putExtra("CLICKED_ITEM_POSTION",getAdapterPosition()); // If one want to get selected item position
startActivity(intent);*/
Toast.makeText(mContext,"You clicked item number "+ViewHolder.this.getAdapterPosition(),Toast.LENTH_SHORT).show();
}
});
}

//This is clean method to bind data with viewHolder. Do all dirty things on View based on dataItem.
//Must be called from onBindViewHolder(),with dataItem. In our case dataItem is String object.
public void bindDataWithViewHolder(DataModel dataItem){
this.mDataItem=dataItem;

if(mDataItem.isSelected()){
llView.setBackgroundColor(Color.ParseColor(SELCTED_COLOR);
}else{
llView.setBackgroundColor(Color.ParseColor(DEFAULT_COLOR);
}
//other View binding logics like setting text , loading image etc.
textView.setText(mDataItem);
}
}
}

正如@Gabriel 在评论中所问的那样,

what if one want to select a single item at time?

在那种情况下,同样不应将所选项目状态保存在 ViewHolder 对象中,因为它会被回收并导致问题。更好的方法是在 Adapter 类中有一个字段 int selectedItemPosition 而不是 ViewHolder 。以下代码 fragment 显示了它。

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



private Context mContext;
private List<DataModel> mRViewDataList;

//variable to hold selected Item position
private int mSelectedItemPosition = -1;


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

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bindDataWithViewHolder(mRViewDataList.get(position),position);
}

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


public class ViewHolder extends RecyclerView.ViewHolder {
private TextView textView;
private LinearLayout llView;
private DataModel mDataItem=null;

public ViewHolder(View itemView) {
super(itemView);
llView=(LinearLayout)itemView.findViewById(R.id.ll_root_view);
textView = (TextView) itemView.findViewById(R.id.tvItemName);
cvItemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Handling for background selection state changed
int previousSelectState=mSelectedItemPosition;
mSelectedItemPosition = getAdapterPosition();
//notify previous selected item
notifyItemChanged(previousSelectState);
//notify new selected Item
notifyItemChanged(mSelectedItemPosition);

//Your other handling in onclick

}
});
}

//This is clean method to bind data with viewHolder. Do all dirty things on View based on dataItem.
//Must be called from onBindViewHolder(),with dataItem. In our case dataItem is String object.
public void bindDataWithViewHolder(DataModel dataItem, int currentPosition){
this.mDataItem=dataItem;
//Handle selection state in object View.
if(currentPosition == mSelectedItemPosition){
llView.setBackgroundColor(Color.ParseColor(SELCTED_COLOR);
}else{
llView.setBackgroundColor(Color.ParseColor(DEFAULT_COLOR);
}
//other View binding logics like setting text , loading image etc.
textView.setText(mDataItem);
}
}
}

如果您只需要维护选定的项目状态,我强烈反对使用 Adapter 类的 notifyDataSetChanged() 方法,因为 RecyclerView 为这些情况提供了更大的灵 active 。

关于android - RecyclerView 在回收时导致问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35265865/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com