RecyclerView Disappearing Images

What I'm trying to create is the horizontal scrolling of the image gallery. I have a RecyclerView (support 22.0.0). The problem I am facing is that when I scroll to the end and then scroll back, usually one image will sometimes be missing two. Oddly enough, when I hold back and forth, another image may be missing. Here is the layout for the item:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="160dp"> <ImageView android:id="@+id/product_variation_image" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:layout_gravity="center"/> 

Here is the Adaper:

 public class TestAdapter extends RecyclerView.Adapter<TestAdapter.ViewHolder> { private String[] mDataset; public static class ViewHolder extends RecyclerView.ViewHolder { public ImageView mImageView; public ViewHolder(View v) { super(v); mImageView = (ImageView) v.findViewById(R.id.product_variation_image); } } public TestAdapter(String[] myDataset) { mDataset = myDataset; } @Override public TestAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // create a new view View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.variaton_list_item, parent, false); ViewHolder vh = new ViewHolder(v); return vh; } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.mImageView.setImageDrawable(null); String url = mDataset[position]; Log.i("TEST", "position = " + position); ((MainActivity)MainActivity.getInstance()).imageDownloader.download(url, holder.mImageView); } @Override public int getItemCount() { return mDataset.length; } 

The upload method retrieves an image from a URL or retrieves it from memory if it was cached. This works great in all other layouts, for example. ListView or GridView. Here is the code that I use to configure it in the snippet:

  final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); mRecyclerView.setLayoutManager(layoutManager); 

This is the onCreateView method. When I get the URLs, I populate them and install the adapter using:

  myDataset[i] = imageURL; // for each image mAdapter = new TestAdapter(myDataset); mRecyclerView.setAdapter(mAdapter); 

An interesting thing is the line in the onBindViewHolder method in the adapter, where I record the position. I found that the cells where the image is not shown are that this method is not being called. It looks like it is skipping this cell for some reason. Even a stranger, if I hold the cell and continue to scroll from left to right, if the cell leaves the screen and then returns, its image, like again, is not called onBindViewHolder method.

+7
android android-recyclerview recycler-adapter
source share
3 answers

One class that I thought didn't matter was what caused the problem. I'm not sure what the reason is, but it is in the custom ImageView class that I use for disposal, which I got from the BitmapFun sample.

  public class RecyclingImageView extends ImageView { public RecyclingImageView(Context context) { super(context); } public RecyclingImageView(Context context, AttributeSet attrs) { super(context, attrs); } /** * @see android.widget.ImageView#onAttachedToWindow() */ @Override protected void onAttachedToWindow() {} /** * @see android.widget.ImageView#onDetachedFromWindow() */ @Override protected void onDetachedFromWindow() { // This has been detached from Window, so clear the drawable setImageDrawable(null); super.onDetachedFromWindow(); } /** * @see android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable) */ @Override public void setImageDrawable(Drawable drawable) { // Keep hold of previous Drawable final Drawable previousDrawable = getDrawable(); // Call super to set new Drawable super.setImageDrawable(drawable); // Notify new Drawable that it is being displayed notifyDrawable(drawable, true); // Notify old Drawable so it is no longer being displayed notifyDrawable(previousDrawable, false); } /** * Notifies the drawable that it displayed state has changed. * * @param drawable * @param isDisplayed */ private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) { if (drawable instanceof RecyclingBitmapDrawable) { // The drawable is a CountingBitmapDrawable, so notify it ((RecyclingBitmapDrawable) drawable).setIsDisplayed(isDisplayed); } else if (drawable instanceof LayerDrawable) { // The drawable is a LayerDrawable, so recurse on each layer LayerDrawable layerDrawable = (LayerDrawable) drawable; for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) { notifyDrawable(layerDrawable.getDrawable(i), isDisplayed); } } } } 

When I replace this with a regular ImageView, I no longer get the problem.

+2
source share

Can I check something? Could you use this library to download images from URLs? http://square.imtqy.com/picasso/ It caches everything and it processes everything asynchronously.

Use it something like ...

 @Override public void onBindViewHolder(ViewHolder holder, int position) { Picasso.with(mImageView.getContext()).cancelRequest(holder.mImageView); String url = mDataset[position]; Picasso.with(mImageView.getContext()).load(url).placeholder(R.drawable.placeholder).into(holder.mImageView); } 

... and see if there are any images left on it. If so, then at least you will be 100% sure that the problem is not with your loading mechanism (which, I think, maybe).

If you are using Android Studio , just add the compile 'com.squareup.picasso:picasso:2.5.2' dependency compile 'com.squareup.picasso:picasso:2.5.2' if you cannot add the library that you will find at the link above.

Worth a try...

+1
source share

We can fix the problem by expanding the LinearLayoutManager and ImageView.

1. Creates a PrecachingLinearLayoutManager

 public class PrecachingLinearLayoutManager extends LinearLayoutManager { private static final int DEFAULT_EXTRA_LAYOUT_SPACE = 600; private int extraLayoutSpace = -1; @SuppressWarnings("unused") private Context mContext; public PrecachingLinearLayoutManager(Context context) { super(context); this.mContext = context; } public PrecachingLinearLayoutManager(Context context, int extraLayoutSpace) { super(context); this.mContext = context; this.extraLayoutSpace = extraLayoutSpace; } public PrecachingLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); this.mContext = context; } public void setExtraLayoutSpace(int extraLayoutSpace) { this.extraLayoutSpace = extraLayoutSpace; } @Override protected int getExtraLayoutSpace(RecyclerView.State state) { if (extraLayoutSpace > 0) { return (extraLayoutSpace); } return (DEFAULT_EXTRA_LAYOUT_SPACE); } } 

2. Use the PrecachingLinearLayoutManager method to replace the LinearLayoutManager

  DisplayMetrics displayMetrics = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); PrecachingLinearLayoutManager layout = new PrecachingLinearLayoutManager(getActivity()); layout.setExtraLayoutSpace(displayMetrics.heightPixels); recyclerview.setLayoutManager(layout); 

3. Creates a RecycleImageView

 private Object tag = null; @Override protected void onAttachedToWindow() { Object tag = getTag(); if (tag == null || !tag.equals(this.tag)) { // Will cause displayed bitmap wrapper to // be 'free-able' setImageDrawable(null); this.tag = null; super.onDetachedFromWindow(); } super.onAttachedToWindow(); } @Override protected void onDetachedFromWindow() { Object tag = getTag(); if (tag != null) { this.tag = tag; } else { // Will cause displayed bitmap wrapper to // be 'free-able' setImageDrawable(null); this.tag = null; super.onDetachedFromWindow(); } } 

4. Use RecycleImageView to replace ImageView

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:extends="http://schemas.android.com/apk/res/com.yourdomain.yourpackage" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/viewgroup" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <com.yourdomain.yourpackage.RecycleImageView android:id="@+id/photo" android:layout_width="40dp" android:layout_height="40dp" extends:delayable="true" android:contentDescription="@string/nothing" android:src="@drawable/photo_placeholder" > </com.yourdomain.yourpackage.RecycleImageView> </LinearLayout> 
0
source share

All Articles