Android 显示错误图像的RecyclerView适配器

Android 显示错误图像的RecyclerView适配器,android,android-layout,android-xml,android-recyclerview,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&l

我有一个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();
    }

}
公共类RecyclerAdapter扩展了RecyclerView.Adapter{ 私有静态语境; 私有列表数据集; 公共RecyclerAdapter(上下文,列表myDataset){ this.context=上下文; this.mDataset=myDataset; } 公共静态类ViewHolder扩展了RecyclerView.ViewHolder实现了View.OnCreateContextMenuListener、View.OnClickListener{ 公共文本视图标题; 公共线路布局占位符; 公共视图持有者(视图){ 超级(视图); view.setOnCreateContextMenuListener(此); title=(TextView)view.findViewById(R.id.title); 占位符=(LinearLayout)view.findViewById(R.id.placeholder); } } @凌驾 public RecyclerAdapter.ViewHolder onCreateViewHolder(视图组父级,int-viewType){ View=LayoutFlater.from(parent.getContext()).flate(R.layout.message\u布局,parent,false); ViewHolder vh=新的ViewHolder((线性布局)视图); 返回vh; } @凌驾 公共无效onBindViewHolder(ViewHolder,int位置){ 消息项=mDataset.get(位置); holder.title.setText(item.getTitle()); int numImages=item.getImages().size(); 如果(数字图像>0){ View test=LayoutInflater.from(holder.placeholder.getContext()).flate(R.layout.images,holder.placeholder,false); ImageView image=(ImageView)test.findViewById(R.id.image); 带(上下文)滑动 .加载(“http://www.website.com/test.png") .fitCenter() .进入(图像); holder.placeholder.addView(测试); } } @凌驾 public int getItemCount(){ 返回mDataset.size(); } } 然而,RecyclerView中的一些项目在不应该显示的情况下显示图像。我怎样才能阻止这种事情发生


我在
onBindViewHolder()中执行检查
if(numImages>0){
,但这仍然不能阻止它显示不应该有图像的项目的图像。

这里的问题是,当您使用将被回收的视图时,您需要在绑定视图时处理所有可能的场景

例如,如果要将ImageView添加到数据源位置0上的LinearLayout,则如果位置4不符合条件,则其视图很可能在绑定位置0时添加ImageView

您可以将R.layout.images内容添加到

R.layout.message\u layout版面的R.id.placeholder并根据具体情况显示/隐藏占位符

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

@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) {
            holder.placeholder.setVisivility(View.VISIBLE);
            ImageView image = (ImageView)holder.placeholder.findViewById(R.id.image);
            Glide.with(context)
                .load("http://www.website.com/test.png")
                .fitCenter()
                .into(image);
        }else{
           holder.placeholder.setVisibility(View.INVISIBLE);
        }
    }

您应该设置
imageView.setImageDrawable(null)
在使用glide设置图像之前,请在
onBindViewHolder()

将image drawable设置为null可修复此问题


希望有帮助!

问题出在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()
调用。

我还遇到了RecyclerView显示错误图像的问题。这是因为RecyclerView没有为每个新列表项扩展视图:相反,列表项正在被回收

通过循环使用视图,我们可以轻松理解克隆视图。克隆的视图可能具有来自上一次交互的图像集

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

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

长话短说,我通过限制RecyclerView克隆我的项目视图解决了这个问题:

在ViewHolder构造函数中设置可循环(false)

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


或者可以选择将图像加载到ViewRecycled上(ViewHolder holde)

我也遇到了同样的问题,我这样修复了它:

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

}
目标:打开视图附加到Windows

@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));
    }
}

我也有同样的问题,并以下面的解决方案结束,它对我来说很好

掌握这个解决方案可能也适合您(将下面的代码放在适配器类中)——

如果您正在使用Kotlin-

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

    override fun getItemViewType(position: Int): Int {
        return position
    }
 @Override
    public long getItemId(int position) {
        return position;
    }

@Override
    public int getItemViewType(int position) {
        return position;
    }
如果您使用的是JAVA-

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

    override fun getItemViewType(position: Int): Int {
        return position
    }
 @Override
    public long getItemId(int position) {
        return position;
    }

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

这在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);
}

我从照片库中获取图片并将其放入GridLayoutManager的recyclerview时遇到了类似的问题(Glide从未遇到过问题)。因此在BindViewHolder适配器中,使用HashMap或SparseIntArray来放置当前的哈希代码(这是回收视图的共同点)然后调用后台任务,在设置映像之前,检查hashcode键(始终将当前适配器位置作为值)是否与第一次调用后台任务时的值(适配器位置)相同

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

有时在使用RecyclerView时,视图可能会被重新使用,并保留以前位置t的大小
@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()));
}