RecyclerView适配器显示错误的图像

时间:2015-11-20 20:51:24

标签: android android-layout android-xml android-recyclerview

我有一个如下所示的RecyclerView适配器:

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

    private static Context context;
    private List<Message> mDataset;

    public RecyclerAdapter(Context context, List<Message> myDataset) {
        this.context = context;
        this.mDataset = myDataset;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener, View.OnClickListener {
        public TextView title;
        public LinearLayout placeholder;

        public ViewHolder(View view) {
            super(view);
            view.setOnCreateContextMenuListener(this);

            title = (TextView) view.findViewById(R.id.title);
            placeholder = (LinearLayout) view.findViewById(R.id.placeholder);
        }
    }

    @Override
    public RecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_layout, parent, false);
        ViewHolder vh = new ViewHolder((LinearLayout) view);

        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Message item = mDataset.get(position);

        holder.title.setText(item.getTitle());

        int numImages = item.getImages().size();

        if (numImages > 0) {
            View test = LayoutInflater.from(holder.placeholder.getContext()).inflate(R.layout.images, holder.placeholder, false);
            ImageView image = (ImageView) test.findViewById(R.id.image);
            Glide.with(context)
                .load("http://www.website.com/test.png")
                .fitCenter()
                .into(image);
            holder.placeholder.addView(test);
        }
    }

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

}

但是,RecyclerView中的某些项目不应显示图像。我怎样才能阻止这种情况发生?

我在if (numImages > 0) {中进行了onBindViewHolder()检查,但仍未阻止它显示不应有图像的项目的图像。

11 个答案:

答案 0 :(得分:17)

你应该设置imageView.setImageDrawable (null) 在使用滑行设置图像之前onBindViewHolder()

将image drawable设置为null可解决此问题。

希望它有所帮助!

答案 1 :(得分:12)

问题出在onBindViewHolder上,这里:

    if (numImages > 0) {
        View test = LayoutInflater.from(holder.placeholder.getContext()).inflate(R.layout.images, holder.placeholder, false);
        ImageView image = (ImageView) test.findViewById(R.id.image);
        Glide.with(context)
            .load("http://www.website.com/test.png")
            .fitCenter()
            .into(image);
        holder.placeholder.addView(test);
    }

如果numImages等于0,则只是允许先前启动的加载进入您重复使用的视图中。完成后,它仍会将旧图像加载到视图中。为防止这种情况,请通过调用clear:

告诉Glide取消之前的加载
   if (numImages > 0) {
        View test = LayoutInflater.from(holder.placeholder.getContext()).inflate(R.layout.images, holder.placeholder, false);
        ImageView image = (ImageView) test.findViewById(R.id.image);
        Glide.with(context)
            .load("http://www.website.com/test.png")
            .fitCenter()
            .into(image);
        holder.placeholder.addView(test);
    } else {
        Glide.clear(image);
    }

当您致电into()时,Glide会为您取消旧的负载。如果您不打电话into(),则必须自己致电clear()

对onBindViewHolder的每次调用都必须包含load()来电或clear()来电。

答案 2 :(得分:9)

我也遇到了RecyclerView显示错误图像的问题。发生这种情况是因为RecyclerView没有为每个新列表项膨胀视图:而是回收列表项。

通过回收视图,我们可以粗略地理解克隆视图。克隆的视图可能具有先前交互的图像集。

如果您正在使用Picasso,Glide或其他lib进行异步加载,这尤其公平。这些库保存对ImageView的引用,并在加载图像时在该引用上设置图像。

到图像加载时,项目视图可能已克隆,图像将设置为错误克隆

总而言之,我通过限制RecyclerView克隆我的项目视图解决了这个问题:

ViewHolder构造函数中的

setIsRecyclable(false)

现在RecyclerView的工作速度有点慢,但至少图像设置正确。

或者在onViewRecycled(ViewHolder holde)

中加载图片

答案 3 :(得分:3)

这里的问题是,当您处理将要回收的视图时,您需要在绑定视图时处理所有可能的方案。

例如,如果您将ImageView添加到数据源位置0的LinearLayout,那么,如果位置4不满足条件,则其视图很可能在绑定位置0时添加了ImageView。

您可以在

中添加R.layout.images内容的内容

R.layout.message_layout 布局的 R.id.placeholder 并根据具体情况显示/隐藏占位符。

因此,您的onBindViewHolder方法将类似于:

{{1}}

答案 4 :(得分:2)

有时使用RecyclerView时,可能会重用View,并保留前一个位置的大小,该位置将更改为当前位置。要处理这些情况,您可以创建一个新的[ViewTarget并将true传递给waitForLayout]:

@Override
public void onBindViewHolder(VH holder, int position) {
  Glide.with(fragment)
    .load(urls.get(position))
    .into(new DrawableImageViewTarget(holder.imageView,/*waitForLayout=*/ true));

https://bumptech.github.io/glide/doc/targets.html

答案 5 :(得分:0)

我有同样的问题,我这样解决了:

目标:onViewAttachedToWindow

@Override
public void onViewAttachedToWindow(Holder holder) {
    super.onViewAttachedToWindow(holder);
    StructAllItems sfi = mArrayList.get(position);   
    if (!sfi.getPicHayatParking().isEmpty()) {
        holder.viewFliperMelk.addSlider(new TextSliderView(mContext.getApplicationContext()).image(T.GET_MELK_IMAGE + '/' + sfi.getPicHayatParking() + ".jpg").setScaleType(BaseSliderView.ScaleType.CenterCrop));
    }
    if (!sfi.getPicSleepRoom().isEmpty()) {
        holder.viewFliperMelk.addSlider(new TextSliderView(mContext.getApplicationContext()).image(T.GET_MELK_IMAGE + '/' + sfi.getPicSleepRoom() + ".jpg").setScaleType(BaseSliderView.ScaleType.CenterCrop));
    }
    if (!sfi.getPicSalonPazirayi().isEmpty()) {
        holder.viewFliperMelk.addSlider(new TextSliderView(mContext.getApplicationContext()).image(T.GET_MELK_IMAGE + '/' + sfi.getPicSalonPazirayi() + ".jpg").setScaleType(BaseSliderView.ScaleType.CenterCrop));
    }
    if (!sfi.getPicNamayeStruct().isEmpty()) {
        holder.viewFliperMelk.addSlider(new TextSliderView(mContext.getApplicationContext()).image(T.GET_MELK_IMAGE + '/' + sfi.getPicNamayeStruct() + ".jpg").setScaleType(BaseSliderView.ScaleType.CenterCrop));
    }
}

答案 6 :(得分:0)

我也遇到了同样的问题,最终得到以下解决方案,它对我来说很好用。.

动手实践此解决方案也可能对您有用(将其放在适配器类中的代码下方)-

如果您使用的是Kotlin-

override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getItemViewType(position: Int): Int {
        return position
    }

如果您使用的是JAVA-

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

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

答案 7 :(得分:0)

这在onBindViewHolder中对我有用!

if(!m.getPicture().isEmpty())
{
    holder.setIsRecyclable(false);
    Picasso.with(holder.profile_pic.getContext()).load(m.getPicture()).placeholder(R.mipmap.ic_launcher_round).into(holder.profile_pic);
    Animation fadeOut = new AlphaAnimation(0, 1);
    fadeOut.setInterpolator(new AccelerateInterpolator());
    fadeOut.setDuration(1000);
    holder.profile_pic.startAnimation(fadeOut);
}
else
{
    holder.setIsRecyclable(true);
}

答案 8 :(得分:0)

从照相馆获取图片并将其放入GridLayoutManager放入recyclerview时,我遇到了类似的问题(Glide从来没有问题)。因此,在适配器onBindViewHolder中,使用HashMap或SparseIntArray将当前的哈希码(这是回收视图的共同点)和适配器的位置放入其中。然后调用您的后台任务,然后完成它,并在设置图像之前,检查哈希码键(始终将当前适配器位置作为该值)是否仍与初次使用时具有相同的值(适配器位置)称为后台任务。

(Global variable)
   private SparseIntArray hashMap = new SparseIntArray();

onBindViewHolder(ViewHolder holder, int position){

   holder.imageView.setImageResource(R.drawable.grey_square);
   hashMap.put(holder.hashCode(), position);
   yourBackgroundTask(ViewHolder holder, int position);
}

yourBackGroundTask(ViewHolder holder, int holderPosition){

   do some stuff in the background.....
      *if you want to stop to image from downloading / or in my case 
       fetching the image from MediaStore then do - 
         if(hashMap.get(holder.hashCode())!=(holderPos)){
                 return null;
             } 
         - in the background task, before the call to get the 
           image

   onPostExecute{
      if(hashMap.get(holder.hashCode())==(holderPosition)){
         holder.imageView.setImageBitmap(result);
      }
   }
}

答案 9 :(得分:0)

所以我只是提供this答案的扩展名,因为没有太多空间可以保留它作为评论了。

尝试上述解决方案之一中提到的方法后,我发现,即使您使用的是静态资源(未下载且可在本地使用),仍然可以解决真正的问题

所以基本上在onBindViewHolder事件上,我只是将资源转换为drawable并如下添加它:

imageView.setImageDrawable(ContextCompat.getDrawable(context,R.drawable.album_art_unknown));

这样,当滑行/异步下载器正在从网络加载实际图像时,视图上将不会有空白空间。

再加上每次我在调用回收器适配器类时还添加了以下代码的情况;

recyclerView.setItemViewCacheSize(10);
recyclerView.setDrawingCacheEnabled(true);

因此,通过使用上述方法,您将不需要设置setIsRecyclable(false),如果您有更大的数据集,则该参数会降低性能。

通过此操作,除了初始加载量外,您还将获得无闪烁的recyclerview加载量。

答案 10 :(得分:0)

我遇到了同样的问题,我通过编写 holder.setIsRecyclable(false) 解决了这个问题。
为我工作。

@Override
public void onBindViewHolder(@NonNull RecylerViewHolder holder, int position) {
    NewsFeed currentFeed = newsFeeds.get(position);
    holder.textView.setText(currentFeed.getNewsTitle());
    holder.sectionView.setText(currentFeed.getNewsSection());
    if(currentFeed.getImageId() == "NOIMG") {
        holder.setIsRecyclable(false);
        Log.v("ImageLoad","Image not loaded");
    } else {
        Picasso.get().load(currentFeed.getImageId()).into(holder.imageView);
        Log.v("ImageLoad","Image id "+ currentFeed.getImageId());
    }
    holder.dateView.setText(getModifiedDate(currentFeed.getDate()));
}