Rahul Chaurasia
Rahul Chaurasia

Reputation: 1641

Picasso image misplacement issue when using with RecyclerView

In my app, I'm showing the articles which are getting fetched from a webservice. Those articles have title, image and other fields. For displaying the images, I'm using Picasso.

Problem - While scrolling the RecyclerView, I'm seeing the images getting misplaced. Some items are having the duplicate images. I know it is RecyclerView which reuses the xml item layout and because of that it is creating a problem. But I think there must be some solution to this problem. I've googled about this and found some posts on stackoverflow but that didn't help. For example-

Picasso loads pictures to the wrong imageview in a list adapter

I've tried the following but having the same problem-

public class ArticleAdapter extends RecyclerView.Adapter<ArticleAdapter.ArticleViewHolder> {

    protected Activity mActivity;
    protected List<Article> mItems = new ArrayList<>();
    private static OnEntityClickCallback mCallback;

    public ArticleAdapter(Activity pActivity) {
        mActivity = pActivity;
    }

    public void setItemClickCallback(OnEntityClickCallback pCallback) {
        this.mCallback = pCallback;
    }

    @Override
public ArticleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View articleView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_article_item, parent, false);
    return new ArticleViewHolder(articleView);
}

    public void addItem(List<Article> moreItems) {
        mItems.addAll(moreItems);
        notifyDataSetChanged();
    }

    public void removeItems(){
        mItems.clear();
        notifyDataSetChanged();
    }


    @Override
    public void onBindViewHolder(ArticleViewHolder holder, int position) {
        holder.bind(mItems.get(position));
    }

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


    static class ArticleViewHolder extends RecyclerView.ViewHolder {

        private TextView tvTitle;
        TextView tvAuthor;
        TextView tvPostdate;
        ImageView imgImage;
        RatingBar ratingBar;

        public ArticleViewHolder(View view) {
            super(view);
            this.tvTitle = (TextView) view.findViewById(R.id.tv_title);
            this.tvAuthor = (TextView) view.findViewById(R.id.tv_author);
            this.tvPostdate = (TextView) view.findViewById(R.id.tv_date);
            this.imgImage = (ImageView) view.findViewById(R.id.img_image);
            this.ratingBar = (RatingBar) view.findViewById(R.id.ratingBar);
        }

       public void bind(final Article article) {

        tvTitle.setText(article.getTitle());
        tvPostdate.setText(article.getPostdate());
        tvAuthor.setText(article.getAuthor());

        // Canceling the older request
        Picasso.with(imgImage.getContext()).cancelRequest(imgImage);
         // Creating a new request.
        if (article.getImage() != null) {
                Picasso.with(imgImage.getContext()).load(article.getImageUrl())
                .placeholder(R.drawable.noimage)
                .error(R.drawable.noimage)
                .into(imgImage);
        }

    this.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mCallback != null)
                mCallback.onEntitySelected(article);
        }
    });
}

    }
}

Update - Part of my layout file

 <ImageView
                    android:id="@+id/img_image"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/cardview_image_thumbnail_height"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentLeft="true"
                    android:scaleType="centerCrop"
                    android:layout_margin="@dimen/cardview_padding_btw_widgets"
                    android:src="@drawable/noimage"
                    />

Any help would be appreciated. Thank you.

Upvotes: 13

Views: 7161

Answers (4)

Shailendra Madda
Shailendra Madda

Reputation: 21551

Also, it worked for me:

@Override
public long getItemId(int position) {
    return position;
}

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

These methods are a cleaner approach to accessing the list's data.

Upvotes: 2

Aur&#233;lien Lepage
Aur&#233;lien Lepage

Reputation: 177

Your view is reused.

Call Picasso.with(imgImage.getContext()).cancelRequest(imgImage); to cancel any existing requests of your ImageView.

Then you can load your image if article.getImage() != null and provide a fallback if your article image is null like imgImage.setImageDrawable(R.drawable.some_drawable);

Upvotes: 0

Ankit Aggarwal
Ankit Aggarwal

Reputation: 5375

Externally set the drawable because you are getting the imageview which has some drawable already attached to it. Imageview is not getting recreated so the previous image in the ImageView will be visible until you remove it.

    if (article.getImage() != null) {
            Picasso.with(imgImage.getContext()).load(article.getImageUrl())
                    .placeholder(R.drawable.noimage)
                    .error(R.drawable.noimage)
                    .into(imgImage);
    }else{
//         imgImage.setImageDrawable(null);
         imgImage.setImageDrawable(R.drawable.some_drawable);
    }

Upvotes: 12

Umer
Umer

Reputation: 1556

Use setTag in onBindViewHolder it will hold data and your images will not misplace.

@Override
    public void onBindViewHolder(ArticleViewHolder holder, int position) {
        holder.bind(mItems.get(position));
        holder.imgImage.setTag(mItems.get(position));
    }

OR

Maybe try your approach maybe it works

@Override
    public void onBindViewHolder(ArticleViewHolder holder, int position) {
        holder.bind.setTag(mItems.get(position));
    }

Upvotes: 0

Related Questions