Search code examples
javaandroidadapterandroid-recyclerviewpalette

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

Solution

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