毕加索:将图片加载到小部件列表视图中

15
我正在实现基于这个教程的ListView小部件:http://laaptu.wordpress.com/2013/07/24/populate-appwidget-listview-with-remote-datadata-from-web/(源代码:https://github.com/laaptu/appwidget-listview/tree/appwidget-listview2/)。
ListView项包含文本和图像,为了加载图像,我使用了Picasso。
getViewAt 实现:
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;
}

当图片加载时,它会破坏布局。

可能的问题是什么?或者我做错了什么?

不加载图片的情况:

screenshot

在加载图片后的样子:

screenshot

更新:

如果我只添加几个嵌套的远程视图(都从一个布局中膨胀),同样的问题也会发生。

更新2:

已报告问题:https://github.com/square/picasso/issues/587

3个回答

23

我自己也尝试解决这个问题,最终决定阻塞后台线程是可以的(使用Picasso的.get()方法),并且成功地在适配器中实现了以下代码:

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

如果你知道返回的图像大小(是否始终相同或者可以预测),你可能还想这样做以减少调整大小的颤动:

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

性能非常流畅,没有任何加载卡顿,我对结果感到非常满意。


1
我必须说,在我的情况下,性能实在是太糟糕了。但是我会点赞这个答案,因为它只需要一点改进。 在调用.get()之前,请调用.fetch(),这样对于同一张图片(当用户上下滚动时)的后续调用就足够快了。 - ievgen
2021 年,这仍然是最好的解决方案。 with(context) 已被弃用。请用 Picasso.get().load(/*URL*/).get() 替换。注意这两个 get 是不同的。 - suhas_sm

15

现在,由于Square团队的贡献,该支持已经被添加。

以下是我如何使用它的方法。

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

享受丝般顺滑的滚动 :)


1
@ab.helly 是的,你说得对。不幸的是,在ListView中这并不起作用,因为Picasso需要回调到主线程。在ListView远程服务中,您已经处于后台线程上了。我也通过将缓存位图存储在映射中并以此方式提供它们来解决了这个问题。我最初使用Picasso获取它们并调整大小以确保最佳性能。 - ngatirauks
如何在RemoteViews工厂中获取mAppWidgetId,例如列表视图。 - Haider Malik

1
您可以使用目标回调来处理您的问题,就像这样。
 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                                }
                        });

更多细节请查看此文章


那个目标将会被非常快速地垃圾回收。Picasso仅持有对目标的弱引用。您应该持有对目标的引用,只要您希望它存在。 - Eric Cochran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接