2

I am trying to use RecylerView which Google introduced recently. I have a set of rows there, 7-8 for now, and each row has a image which I am getting from server. For this I am using Picasso library but this is not working for me. I am not sure if I am missing something or configure something.

The screen is correctly showing the default image on each row but doesn't download image from server and I wait more than 5 minutes if slow response from server but that is not the case.

Code

public DemoRecyclerAdapter(List<DemoRowModel> items, int itemLayout, final Context mContext) {
    this.items = items;
    this.itemLayout = itemLayout;
    this.mContext = mContext;
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
    return new ViewHolder(v);
}

@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
    DemoRowModel item = items.get(position);

    holder.mDemoNameTextView.setText(item.getDemoName());
    holder.mDemoDateTextView.setText(item.getDemoDate());

    Target mTarget = new Target() {
        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
            holder.mImageView.setImageBitmap(bitmap);
        }

        @Override
        public void onBitmapFailed(Drawable drawable) {
            Logger.d(TAG, "Failed! Bitmap could not downloaded.");
        }

        @Override
        public void onPrepareLoad(Drawable drawable) {
        }
    };

    Picasso.Builder builder = new Picasso.Builder(mContext);
    Picasso picasso = builder.downloader(new OkHttpDownloader(mContext) {
        @Override
        protected HttpURLConnection openConnection(Uri uri) throws IOException {
            HttpURLConnection connection = super.openConnection(uri);
            // fetch the auth value
            SharedPreferences mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext.getApplicationContext());
            connection.setRequestProperty(Constant.HEADER_X_API_KEY, mSharedPreferences.getString(SharedPreferenceKeys.JSESSIONID, ""));
            return connection;
        }
    }).build();

    picasso.load(item.getImagePath()).into(mTarget);

    // here set the value
    holder.itemView.setTag(item);

}

Thanks in advance.

rekire
  • 47,260
  • 30
  • 167
  • 264
N Sharma
  • 33,489
  • 95
  • 256
  • 444
  • As one answer below points out: "Another way: hold Target object in ViewHolder." - I (think I) have successfully used a `ViewHolder` that does not even hold a reference to the ImageView - only to a `Target`. See here: http://stackoverflow.com/questions/29717617/picasso-target-recyclerview-is-my-implementation-correct – david.mihola May 25 '15 at 17:54

2 Answers2

5

If you are using Target's - they should be strong referenced objects. Create field mTaget in your class and move Target's initialization from onBindViewHolder method.

Edit: hold auth keys in secure place, like keystore. Don't save them in SharedPreferences, it's a bad practice.

Update:

1) create custom Target class

public class CommonImageTarget implements Target {
    private final ImageView imageView;

    public CommonImageTarget(final ImageView imageView) {
        this.imageView = imageView;
    }

    @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        this.imageView.setImageBitmap(bitmap);
    }

    @Override
    public void onBitmapFailed(Drawable errorDrawable) {
        Logger.d(TAG, "Failed! Bitmap could not downloaded.");
    }

    @Override
    public void onPrepareLoad(Drawable placeHolderDrawable) {
    }
}

2) create custom ImageView

public class ImageViewWithTarget extends ImageView{
    /**
     * Required for Bitmap loading using Picasso. Picasso uses weak references in into() method and Target's are garbage collected, save them in object.
     */
    private Target target;

    public ImageViewWithTarget(Context context) {
        super(context);
    }

    public ImageViewWithTarget(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ImageViewWithTarget(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public Target getTarget() {
        return target;
    }

    public void setTarget(Target target) {
        this.target = target;
    }
}

3) when you initialize your imageView in viewHolder, set custom Target in it

viewHolder.mImageView.setTarget(new CommonImageTarget(viewHolder.mImageView));

4) update ViewHolder class

public class ViewHolder{
       ...
       private ImageViewWithTarget mImageView;
   }

5) replace ImageView with ImageViewWithTarget in your layout

6) update onBindViewHolder method

picasso.load(item.getImagePath()).into(viewHolder.mImageView.getTarget());

Now every ImageView will hold own Target object, and Target isn't garbage collected.

Another way: hold Target object in ViewHolder.

Veaceslav Gaidarji
  • 4,261
  • 3
  • 38
  • 61
  • I didn't get you exactly "move Target's initialization from onBindViewHolder method." – N Sharma Nov 20 '14 at 10:39
  • you create your mTarget object inside onBindViewHolder. Don't do that. – Veaceslav Gaidarji Nov 20 '14 at 10:45
  • then where should I create it ? – N Sharma Nov 20 '14 at 10:46
  • Ok thanks so much I will try in next 5-6 hours and update you. if this work then sure I will accept it :) – N Sharma Nov 20 '14 at 11:13
  • good. Keep in mind the main idea - Target object is garbage collected, to prevent this - save it your model object or somewhere in another place, but don't create local Target variable in onBindViewHolder. – Veaceslav Gaidarji Nov 20 '14 at 11:19
  • Thanks +1 later I really want to discuss with you thanks so much :) – N Sharma Nov 20 '14 at 19:07
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/65311/discussion-between-veaceslav-gaidarji-and-williams). – Veaceslav Gaidarji Nov 20 '14 at 20:25
  • Just came across this. @VeaceslavGaidarji answer is great. I tried playing around with it. So I just implemented the `Target` class in my `TextView` to keep a strong reference and load the image properly without any problem. It works like charm – Akbarsha Aug 05 '16 at 14:39
2

You should use method setTag(Object tag) of ImageView

This case:

......

Target mTarget = new Target() {
    @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
        holder.mImageView.setImageBitmap(bitmap);
    }

    @Override
    public void onBitmapFailed(Drawable drawable) {
        Logger.d(TAG, "Failed! Bitmap could not downloaded.");
    }

    @Override
    public void onPrepareLoad(Drawable drawable) {
    }
};

holder.mImageView.setTag(mTarget);// Target isn't garbage collected

Picasso.Builder builder = new Picasso.Builder(mContext);

......

it's worked for me

gianguyen
  • 221
  • 3
  • 7