Rahul Chaurasia Rahul Chaurasia - 1 month ago 7
Android Question

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.

Answer

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