Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent RecyclerView showing previous content when scrolling

I have a RecyclerView with a GridLinearLayout and a custom adapter. The content of each item is a picture downloaded using a json and parsing it.

Basically is a grid of pictures.

Everything works almost fine, but however, when scrolling down the content, and the going up again, it shows the previous views in each item for less than a second, and then show the proper picture again.

What could I do to prevent or fix this? Thanks in advance for any help and/or guidance you could provide.

This is the adapter code:

package jahirfiquitiva.project.adapters;

import android.content.Context;
import android.graphics.Bitmap;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

import jahirfiquitiva.project.activities.WallpapersActivity;
import com.koushikdutta.async.future.FutureCallback;
import com.koushikdutta.ion.Ion;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

import jahirfiquitiva.project.R;

public class WallpapersAdapter extends RecyclerView.Adapter<WallpapersAdapter.WallsHolder> {

    public interface ClickListener {
        void onClick(WallsHolder view, int index, boolean longClick);
    }

    private ArrayList<HashMap<String, String>> data;
    private final Context context;
    private boolean usePalette = true;
    private final ClickListener mCallback;
    private final Map<String, Palette> mPaletteCache = new WeakHashMap<>();

    public WallpapersAdapter(Context context, ClickListener callback) {
        this.context = context;
        this.data = new ArrayList<>();
        this.mCallback = callback;
    }

    public void setData(ArrayList<HashMap<String, String>> data) {
        this.data = data;
        notifyDataSetChanged();
    }

    @Override
    public WallsHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(context);
        return new WallsHolder(inflater.inflate(R.layout.wallpaper_item, parent, false));
    }

    @Override
    public void onBindViewHolder(final WallsHolder holder, int position) {
        Animation anim = AnimationUtils.loadAnimation(context, android.R.anim.fade_in);
        HashMap<String, String> jsondata = data.get(position);

        holder.name.setText(jsondata.get(WallpapersActivity.NAME));
        final String wallurl = jsondata.get(WallpapersActivity.WALL);
        holder.wall.startAnimation(anim);
        holder.wall.setTag(wallurl);

        Ion.with(context)
                .load(wallurl)
                .asBitmap()
                .setCallback(new FutureCallback<Bitmap>() {
                    @Override
                    public void onCompleted(Exception e, Bitmap result) {
                        holder.progressBar.setVisibility(View.GONE);
                        if (e != null) {
                            e.printStackTrace();
                        } else if (holder.wall.getTag() != null && holder.wall.getTag().equals(wallurl)) {
                            holder.wall.setImageBitmap(result);
                            if (usePalette) {
                                Palette p;
                                if (mPaletteCache.containsKey(wallurl)) {
                                    p = mPaletteCache.get(wallurl);
                                } else {
                                    p = new Palette.Builder(result).generate();
                                    mPaletteCache.put(wallurl, p);
                                }
                                if (p != null) {
                                    Palette.Swatch wallSwatch = p.getVibrantSwatch();
                                    if (wallSwatch != null) {
                                        holder.titleBg.setBackgroundColor(wallSwatch.getRgb());
                                        holder.titleBg.setAlpha(1);
                                        holder.name.setTextColor(wallSwatch.getTitleTextColor());
                                        holder.name.setAlpha(1);
                                    }
                                }
                            }
                        }
                    }
                });
    }

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

    public class WallsHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {

        public final View view;
        public final ImageView wall;
        public final TextView name;
        public final ProgressBar progressBar;
        public final LinearLayout titleBg;

        WallsHolder(View v) {
            super(v);
            view = v;
            wall = (ImageView) v.findViewById(R.id.wall);
            name = (TextView) v.findViewById(R.id.name);
            progressBar = (ProgressBar) v.findViewById(R.id.progress);
            titleBg = (LinearLayout) v.findViewById(R.id.titlebg);

            view.setOnClickListener(this);
            view.setOnLongClickListener(this);
        }

        @Override
        public void onClick(View v) {
            int index = getLayoutPosition();
            if (mCallback != null)
                mCallback.onClick(this, index, false);
        }

        @Override
        public boolean onLongClick(View v) {
            int index = getLayoutPosition();
            if (mCallback != null)
                mCallback.onClick(this, index, true);
            return false;
        }
    }
}
like image 262
Jahir Fiquitiva Avatar asked Jul 02 '15 05:07

Jahir Fiquitiva


2 Answers

Overwrite onViewRecycled (VH holder) to set the image to null.

Like this:

public void onViewRecycled (VH holder) {

   holder.wall.setImageBitmap(null);
}
like image 137
Ralph Bergmann Avatar answered Sep 17 '22 19:09

Ralph Bergmann


As the name suggests RecyclerView recycles the views to optimize memory so it displays the content of previous view. Since you are loading the image from internet it takes little time to load the image so the content of previous image could be observed. You can do one of the following things.

1) Set a default image from local resource before loading actual image, preferably a small size image to preserve memory.something like this

//load default image first                 
holder.wall.setImageResource(R.id.your_default_image_resource);
//load actual image
Ion.with(context)
            .load(wallurl)
    .asBitmap()
    .setCallback(new FutureCallback<Bitmap>() {
        @Override
        public void onCompleted(Exception e, Bitmap result) {
            holder.progressBar.setVisibility(View.GONE);
            if (e != null) {
                e.printStackTrace();
            } else if (holder.wall.getTag() != null && holder.wall.getTag().equals(wallurl)) {
                holder.wall.setImageBitmap(result);
                if (usePalette) {
                    Palette p;
                    if (mPaletteCache.containsKey(wallurl)) {
                        p = mPaletteCache.get(wallurl);
                    } else {
                        p = new Palette.Builder(result).generate();
                        mPaletteCache.put(wallurl, p);
                    }
                    if (p != null) {
                        Palette.Swatch wallSwatch = p.getVibrantSwatch();
                        if (wallSwatch != null) {
                            holder.titleBg.setBackgroundColor(wallSwatch.getRgb());
                            holder.titleBg.setAlpha(1);
                            holder.name.setTextColor(wallSwatch.getTitleTextColor());
                            holder.name.setAlpha(1);
                        }
                    }
                }
            }
        }
    });

2) Set the ImageView visibility to GONE/INVISIBLE before loading the image and make it VISIBLE again after the image is loaded.

 //hide the imageview
holder.wall.setVisibility(View.INVISIBLE);
Ion.with(context)
            .load(wallurl)
    .asBitmap()
    .setCallback(new FutureCallback<Bitmap>() {
        @Override
        public void onCompleted(Exception e, Bitmap result) {
            holder.progressBar.setVisibility(View.GONE);
            if (e != null) {
                e.printStackTrace();
            } else if (holder.wall.getTag() != null && holder.wall.getTag().equals(wallurl)) {
                //show the imageview and set bitmap
                holder.wall.setVisibility(View.VISIBLE);
                holder.wall.setImageBitmap(result);
                if (usePalette) {
                    Palette p;
                    if (mPaletteCache.containsKey(wallurl)) {
                        p = mPaletteCache.get(wallurl);
                    } else {
                        p = new Palette.Builder(result).generate();
                        mPaletteCache.put(wallurl, p);
                    }
                    if (p != null) {
                        Palette.Swatch wallSwatch = p.getVibrantSwatch();
                        if (wallSwatch != null) {
                            holder.titleBg.setBackgroundColor(wallSwatch.getRgb());
                            holder.titleBg.setAlpha(1);
                            holder.name.setTextColor(wallSwatch.getTitleTextColor());
                            holder.name.setAlpha(1);
                        }
                    }
                }
            }
        }
    });
like image 31
Abhishek V Avatar answered Sep 20 '22 19:09

Abhishek V