我有一个如下所示的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()
检查,但仍未阻止它显示不应有图像的项目的图像。
答案 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));
答案 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()));
}