15

I'm implementing widget with listview based on this tutorial: http://laaptu.wordpress.com/2013/07/24/populate-appwidget-listview-with-remote-datadata-from-web/ (source code: https://github.com/laaptu/appwidget-listview/tree/appwidget-listview2/).

ListView item contains text & image, for image loading I use Picasso.

getViewAt implementation:

public RemoteViews getViewAt(int position) {
    final RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.listview_item);
    ListItem listItem = listItemList.get(position);
    remoteView.setTextViewText(R.id.headline, listItem.headline);
    final String imageUrl = listItem.image;
    handler.post(new Runnable() {
        @Override
        public void run() {
            if (!Utils.isEmpty(imageUrl)) {
                picasso.load(imageUrl)
                        .placeholder(R.drawable.empty_photo)
                        .into(remoteView, R.id.picture, new int[] { appWidgetId });
            }
        }
    });

    return remoteView;
}

When the image loaded, it breaks layout.

What can be a possible problem? Or what am I doing wrong?

Without image loading:

screenshot

How it looks after image is loaded:

screenshot

UPDATE:

The same happens if I just add a few nested remote views (all inflated from one layout)

UPDATE2:

Reported an issue: https://github.com/square/picasso/issues/587

ab.helly
  • 517
  • 1
  • 5
  • 21

3 Answers3

23

After struggling with this myself, I decided that blocking the background thread was ok (using .get() with Picasso) and was able to get the following to work inside my adapter:

    @Override
    public RemoteViews getViewAt(int position) {
        DBItem item = list.get(position);
        RemoteViews view = new RemoteViews(context.getPackageName(), R.layout.widget_item);
        try {
            Bitmap b = Picasso.with(context).load(item.getImageUrl()).get();
            view.setImageViewBitmap(R.id.widget_image, b);
        } catch (IOException e) {
            e.printStackTrace();
        }
        view.setTextViewText(R.id.widget_title, item.getTitle());
        return view;
    }

If you know the size of the image coming back (if it's always the same or you can predict it), you might want to do something like this as well to cut down on resize jitters:

    @Override
    public RemoteViews getLoadingView() {
        return new RemoteViews(context.getPackageName(), R.layout.v2_widget_item_loading);
    }

Performance is very smooth, no loading jitters, I'm pretty happy with the result.

wblaschko
  • 3,252
  • 1
  • 18
  • 24
  • 1
    I should say the performance was awful in my case. But I +1 this answer because it just needs a little improvement. Before calling .get() invoke .fetch() so subsequent calls fro the same picture (as the user scroll up and down) will be fast enough. – ievgen Mar 15 '19 at 13:50
  • 2021, this is still the best solution out there. `with(context)` is deprecated. Change it with `Picasso.get().load(/*URL*/).get()` Notice the two gets? Both are different. – suhas_sm Aug 11 '21 at 11:16
15

This support has been added now, thanks to the great guys at square

This is how I've used it

Picasso.with(mContext)
.load(imagePath)
.into(remoteViews, R.id.some_id, new int[] {mAppWidgetId});

Enjoy the buttery smooth scrolling :)

Volodymyr Kulyk
  • 6,455
  • 3
  • 36
  • 63
ngatirauks
  • 779
  • 8
  • 18
  • 1
    @ab.helly yes your correct. This doesn't work within a listview unfortunately as Picasso needs to call back onto the main thread. In a listview remote service, your already on a background thread. I've also worked around this, by storing cached bitmaps in a map and serving them up that way. I originally get them using Picasso and resize to ensure optimal performance. – ngatirauks Feb 09 '15 at 20:50
  • How do you get mAppWidgetId in remote views factory eg. list view – Haider Malik Apr 05 '21 at 21:35
1

you can use Target Callback to handle your issue like this

 Picasso.with(mContext)
                        .load(imageUrl)
                        .placeholder(R.drawable.defimage)
                        .into(new Target() {
                            @Override
                            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                                mRemoteViews.setImageViewBitmap(R.id.myImage,bitmap);
                            }

                            @Override
                            public void onBitmapFailed(Drawable errorDrawable) {
                                //do something when loading failed 
                            }

                            @Override
                            public void onPrepareLoad(Drawable placeHolderDrawable) {
                               //do something while loading                                }
                        });

for more details check this article

3zcs
  • 35
  • 4
  • 18
  • That Target is going to get garbage collected really fast. Picasso holds only a weak reference to Targets. You should hold a reference to your Target for as long as you want it to live. – Eric Cochran Feb 24 '17 at 19:08