【问题标题】:Recyclerview items mixing up on refreshRecyclerview 项目在刷新时混合
【发布时间】:2018-07-17 16:14:51
【问题描述】:

可能重复RecyclerView Mixing Up Items

我有一个 Recyclerview,我通过适配器和列表填充项目。问题是,在刷新(通过 SwipeRefreshLayout)时,倒数计时器的数据混合在一起,并且错误的值显示在另一个项目上,如下图所示。项目 1 突出显示的计时器应该显示“已过期”,但在刷新时它会采用项目 2 的计时器值

这是我的适配器中的 onBindViewHolder 代码。我是否正确实施它?

public class FeedAdapter extends RecyclerView.Adapter<FeedAdapter.FeedModelViewHolder> {
private List<FeedModel> feedModelList;
private MainFeedListener listener;
private Context mContext;
String cPrice;

public FeedAdapter(List<FeedModel>feedModelList, Context context, MainFeedListener bidFeedListener) {
    this.feedModelList = feedModelList;
    this.mContext = context;
    this.listener = bidFeedListener;
}

@NonNull
@Override
public FeedModelViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_bid_feed, parent, false);
    return new FeedModelViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull final FeedModelViewHolder holder, final int position) {
    final FeedModel feedModel = feedModelList.get(position);
    ((TextView) holder.bidView.findViewById(R.id.bid_title)).setText(feedModel.getTitle());
    ((TextView) holder.bidView.findViewById(R.id.start_price)).setText(feedModel.getCurrency() +" "+Convert(feedModel.getStartPrice()));
    ((TextView) holder.bidView.findViewById(R.id.tags)).setText(feedModel.getTags());
    ((TextView) holder.bidView.findViewById(R.id.location)).setText(feedModel.getLocation());


    new CountDownTimer(feedModel.getDeadline(), 1000) {
        public void onTick(long millisUntilFinished) {
            ((TextView) holder.bidView.findViewById(R.id.deadline)).setText(formatMilliSecondsToTime( millisUntilFinished));
            feedModel.setTime(millisUntilFinished);
        }
        public void onFinish() {
            ((TextView) holder.bidView.findViewById(R.id.deadline)).setText("EXPIRED");
        }

    }.start();

    if (!TextUtils.isEmpty(feedModel.getImageUrl())) {
        Glide.with(mContext).load(feedModel.getImageUrl())
                .thumbnail(0.5f)
                .crossFade()
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .into((ImageView) holder.bidView.findViewById(R.id.bid_thumbnail));
     }
    ((LinearLayout) holder.bidView.findViewById(R.id.article_card_root)).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
           listener.onBidRowClicked(feedModel);
        }
    });
}


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

public static class FeedModelViewHolder extends RecyclerView.ViewHolder {
    private View bidView;
    public FeedModelViewHolder(View v) {
        super(v);
        bidView = v;
    }
}


String Convert(Double d){
    int i;
    d +=0.005;
    i= (int) (d*100);
    Double b = (double) (i / 100);
    return b.toString();
}    

public static  String formatMilliSecondsToTime(long milliseconds) {

    int seconds = (int) (milliseconds / 1000) % 60;
    int minutes = (int) ((milliseconds / (1000 * 60)) % 60);
    int hours = (int) ((milliseconds / (1000 * 60 * 60))); //
  //  int days = (int) ((milliseconds / (1000 * 60 * 60)) % 24); //
    return twoDigitString(hours) + ":" + twoDigitString(minutes) + ":"
            + twoDigitString(seconds);
}

private static String twoDigitString(long number) {

    if (number == 0) {
        return "00";
    }

    if (number / 10 == 0) {
        return "0" + number;
    }

    return String.valueOf(number);
}

}

我使用 Volley 库填充列表并通过构造函数将列表传递给适配器

【问题讨论】:

    标签: android android-recyclerview


    【解决方案1】:

    请将您的持有人类别更改为:

     public static class FeedModelViewHolder extends RecyclerView.ViewHolder {
            private View bidView;
            private TextView title;
            private TextView price;
            private TextView tags;
            private TextView location;
           public FeedModelViewHolder(View v) {
                super(v);
            title = v.findViewById(R.id.bid_title);
            price = v.findViewById(R.id.start_price)
            tags = v.findViewById(R.id.tags)
            location= v.findViewById(R.id.location);
            bidView = v;
            }
        }
    

    在 BindViewHolder 上

    @Override
    public void onBindViewHolder(@NonNull final FeedModelViewHolder holder, final int position) {
        final FeedModel feedModel = feedModelList.get(position);
       holder,title.setText(feedModel.getTitle());
        holder.price.setText(feedModel.getCurrency() +" "+Convert(feedModel.getStartPrice()));
        holder.tags.setText(feedModel.getTags());
        holder.location.setText(feedModel.getLocation());
    }
    

    【讨论】:

    • 你好@faran,我是否认为这种方法主要是为了可读性?
    • 是的!但是 ViewHolder 模式也会这样做以防止 findViewById() 被多次调用无用,将视图保持在静态引用上,这是节省一些资源的好模式(特别是当您需要在列表视图项中引用许多视图)。
    • 非常感谢您提供的信息性描述和帮助
    【解决方案2】:

    这个问题很简单。

    RecyclerView 复用了 holder,每次调用 bind 来更新里面的数据。

    由于每次绑定任何数据时都会创建一个倒数计时器,因此您最终会得到多个计时器来更新同一个视图持有者。

    最好将 FeedViewHolder 中的倒数计时器作为参考移动,在绑定数据(如果已启动)之前取消它并重新安排到所需的持续时间。

    public void onBindViewHolder(final FeedViewHolder holder, final int position) {
    ...
    if (holder.timer != null) {
        holder.timer.cancel();
    }
    holder.timer = new CountDownTimer(expiryTime, 500) {
        ...
    }.start();
     }
    
    public static class FeedViewHolder extends RecyclerView.ViewHolder {
    ...
    CountDownTimer timer;
    
    public FeedViewHolder(View itemView) {
        ...
     }
     }
    

    【讨论】:

    • 太棒了,这意味着我需要在每次刷新之前取消计时器,对吗?
    • 它有效,我将它与@Viktor 进行比较,因为我认为这是我接受之前最好的方法
    • 这个方法是解决我的问题的最简单有效的方法。非常感谢您对我的教育。
    【解决方案3】:

    您需要更多地使用onCreateViewHolder。首先(不是真正的问题,而是可读性提示)将您的视图绑定到字段

    public class FeedModelViewHolder extends RecyclerView.ViewHolder {
    
        TextView bigTitle;
        TextView deadline;
        ...
    
        public FeedModelViewHolder(View v) {
            super(v);
    
            bitTitle =  = (TextView) v.findViewById(R.id.bid_title);
            deadline =  = (TextView) v.findViewById(R.id.deadline);
            ...
    
            new CountDownTimer(feedModel.getDeadline(), 1000) {
                public void onTick(long millisUntilFinished) {
                    dealine.setText(formatMilliSecondsToTime( millisUntilFinished));
                    feedModelList.get(getAdapterPosition()).setTime(millisUntilFinished);
                }
                public void onFinish() {
                    dealine.setText("EXPIRED");
                }
    
            }.start();
        }
    }
    

    然后在onCreateViewHolder 中再次初始化您的计时器,因为它只被调用一次。 onBindViewHolder 在开始时和每次重绘时都会被调用。

    但是,这种方法会在创建视图时启动计时器,并且可能不会产生理想的结果。建议在 Adapter 之外的某个地方跟踪此时间,然后将引用传递给它们。

    【讨论】:

    • 在 FeedModelViewHolder() 中放置 CountDownTimer() 方法需要进行一些调整,例如使列表静态或 FeedModelViewHolder 非静态等,这样是否正确?
    • FeedModelViewHolder 实际上不需要是静态的。我更新了删除 static 类型的答案 - stackoverflow.com/questions/40584424/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-03
    • 2020-05-02
    • 1970-01-01
    • 2015-06-10
    • 2020-11-09
    相关资源
    最近更新 更多