Android 使用毕加索将位图加载到循环中的菜单项

Android 使用毕加索将位图加载到循环中的菜单项,android,picasso,Android,Picasso,下面的代码是在循环中调用的。我读过,但是,由于我无法将setTag设置为MenuItem,Target会被垃圾回收onBitmapLoaded。我怎样才能解决这个问题 另一个问题是,在首次发布应用程序时,它不起作用。我再次调用此方法后,它是如何工作的 private void addServiceToMenu(Service service, final MenuItem menuItem) { if (!TextUtils.isEmpty(service.getIconURL()))

下面的代码是在循环中调用的。我读过,但是,由于我无法将
setTag
设置为
MenuItem
Target
会被垃圾回收<未调用code>onBitmapLoaded。我怎样才能解决这个问题

另一个问题是,在首次发布应用程序时,它不起作用。我再次调用此方法后,它是如何工作的

private void addServiceToMenu(Service service, final MenuItem menuItem) {
    if (!TextUtils.isEmpty(service.getIconURL())) {
        Resources resources = getResources();
        final int targetWidth = resources.getDimensionPixelSize(R.dimen.menu_icon_size);
        final int targetHeight = resources.getDimensionPixelSize(R.dimen.menu_icon_size);

        final Target target = new Target() {
            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                Drawable drawable = new BitmapDrawable(getResources(), bitmap);

                drawable.setBounds(0, 0, targetWidth, targetHeight);
                menuItem.setIcon(drawable);
            }

            @Override
            public void onBitmapFailed(Drawable errorDrawable) { }

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

        Picasso.with(MainActivity.this).load(service.getIconURL())
                .resize(targetWidth, targetHeight)
                .into(target);
    }
}

创建一个类,在该类中可以保留对对象的强引用

完整工作示例:

public class MainActivity extends AppCompatActivity {

  private final List<Service> services = new ArrayList<>();

  {
    // add arbitrary data just for the example
    services.add(new Service("Android",
        "https://github.com/google/material-design-icons/raw/master/action/drawable-xhdpi/ic_android_black_24dp.png"));
    services.add(new Service("Account",
        "https://github.com/google/material-design-icons/raw/master/action/drawable-xhdpi/ic_account_circle_black_24dp.png"));
    services.add(new Service("Shopping",
        "https://github.com/google/material-design-icons/raw/master/action/drawable-xhdpi/ic_add_shopping_cart_black_24dp.png"));
  }

  @Override public boolean onCreateOptionsMenu(Menu menu) {
    for (Service service : services) {
      // create a few MenuItems. Normally done in XML.
      MenuItem menuItem = menu.add(service.getName());
      menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
      // load the icon using Picasso
      addServiceToMenu(service, menuItem);
    }
    return super.onCreateOptionsMenu(menu);
  }

  private void addServiceToMenu(Service service, final MenuItem menuItem) {
    if (!TextUtils.isEmpty(service.getIconURL())) {
      Resources resources = getResources();
      final int targetWidth = resources.getDimensionPixelSize(R.dimen.menu_icon_size);
      final int targetHeight = resources.getDimensionPixelSize(R.dimen.menu_icon_size);
      final MenuItemIconLoader loader = new MenuItemIconLoader(menuItem, targetHeight, targetWidth);
      loader.load(MainActivity.this, service);
    }
  }

  class MenuItemIconLoader {

    private final WeakReference<MenuItem> itemWeakReference;
    private final int targetHeight;
    private final int targetWidth;

    public MenuItemIconLoader(MenuItem menuItem, int targetHeight, int targetWidth) {
      this.itemWeakReference = new WeakReference<>(menuItem);
      this.targetHeight = targetHeight;
      this.targetWidth = targetWidth;
    }

    private final Target target = new Target() {

      @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        MenuItem menuItem = itemWeakReference.get();
        if (menuItem != null) {
          Drawable drawable = new BitmapDrawable(getResources(), bitmap);
          drawable.setBounds(0, 0, targetWidth, targetHeight);
          menuItem.setIcon(drawable);
        }
      }

      @Override public void onBitmapFailed(Drawable errorDrawable) {

      }

      @Override public void onPrepareLoad(Drawable placeHolderDrawable) {

      }
    };

    public void load(Context context, Service service) {
      Picasso.with(context).load(service.getIconURL()).resize(targetWidth, targetHeight).into(target);
    }

  }

  static class Service {

    private String name;
    private String iconUrl;

    public Service(String name, String iconUrl) {
      this.name = name;
      this.iconUrl = iconUrl;
    }

    public String getName() {
      return name;
    }

    public String getIconURL() {
      return iconUrl;
    }

  }

}
public类MainActivity扩展了AppCompatActivity{
private final List services=new ArrayList();
{
//仅为示例添加任意数据
添加(新服务(“Android”,
"https://github.com/google/material-design-icons/raw/master/action/drawable-xhdpi/ic_android_black_24dp.png"));
添加(新服务(“帐户”),
"https://github.com/google/material-design-icons/raw/master/action/drawable-xhdpi/ic_account_circle_black_24dp.png"));
添加(新服务(“购物”),
"https://github.com/google/material-design-icons/raw/master/action/drawable-xhdpi/ic_add_shopping_cart_black_24dp.png"));
}
@重写公共布尔onCreateOptions菜单(菜单菜单){
用于(服务:服务){
//创建一些菜单项。通常用XML完成。
MenuItem MenuItem=menu.add(service.getName());
menuItem.setShowAsAction(menuItem.ShowAsAction总是);
//使用毕加索加载图标
addServiceToMenu(服务,菜单项);
}
返回super.onCreateOptions菜单(菜单);
}
专用void addServiceToMenu(服务服务,最终菜单项菜单项){
如果(!TextUtils.isEmpty(service.getIconURL())){
Resources=getResources();
final int targetWidth=resources.getDimensionPixelSize(R.dimen.menu\u icon\u size);
final int targetLight=resources.getDimensionPixelSize(R.dimen.menu\u icon\u size);
final menuitOnLoader loader=新建menuItem loader(menuItem、TargetSight、targetWidth);
loader.load(MainActivity.this,service);
}
}
类MenuItemIconLoader{
私有最终WeakReference项WeakReference;
私人最终int targetHeight;
私人最终int targetWidth;
公共MenuItemLoader(MenuItem MenuItem、int-TargetLight、int-targetWidth){
this.itemWeakReference=新WeakReference(menuItem);
this.targetEight=targetEight;
this.targetWidth=targetWidth;
}
私有最终目标=新目标(){
@覆盖BitMapLoaded(位图位图,Picasso.LoadedFrom)上的公共空白{
MenuItem MenuItem=itemWeakReference.get();
if(menuItem!=null){
Drawable Drawable=新的BitmapDrawable(getResources(),位图);
可拉伸。缩位(0,0,目标宽度,目标宽度);
菜单项设置图标(可绘制);
}
}
@重写公共void onBitmapFailed(Drawable errorDrawable){
}
@覆盖PrepareLoad上的公共void(可提取占位符可提取){
}
};
公共无效加载(上下文、服务){
毕加索.with(context).load(service.getIconURL()).resize(targetWidth,targetSight).into(target);
}
}
静态类服务{
私有字符串名称;
私有字符串Iconur;
公共服务(字符串名称、字符串iconUrl){
this.name=名称;
this.iconUrl=iconUrl;
}
公共字符串getName(){
返回名称;
}
公共字符串getIconURL(){
返回Iconur;
}
}
}
我已经实现了@Jared的,但没有成功。所以我决定把所有的
MenuItemIconLoader
s都保存在一个列表中,它成功了。我想我的问题是对的:

是什么将“MenuItemOnLoader”保留在内存中

以下是我的解决方案:

List<Service> mServices = new ArrayList<>();
//To keep them in memory
final List<MenuItemIconLoader> mIconLoaderList = new ArrayList<>();
....
//
for (int i = 0; i < mServices.size(); i++) {
    Service service = mServices.get(i);
    final MenuItem menuItem = menu.add(SERVICES, Menu.NONE + i, i + 1, service.getTitle())
            .setCheckable(true);
    mIconLoaderList.add(new MenuItemIconLoader(MainActivity.this, menuItem));
}
addServicesToMenu();

....
private void addServicesToMenu() {
    for (int i = 0; i < mServices.size(); i++) {
        Service service = mServices.get(i);
        MenuItemIconLoader iconLoader = mIconLoaderList.get(i);

        if (!TextUtils.isEmpty(service.getIconURL())) {
            iconLoader.load(service);
        }
    }
}
List mServices=new ArrayList();
//把它们留在记忆中
最终列表mIconLoaderList=新的ArrayList();
....
//
对于(int i=0;i
不过,肯定有一些合理的地方可以保存
目标
参考。作为具有此方法的对象上的字段?某些活动、控制器或视图?至于另一个问题,第一次回调没有返回的原因是加载时,
Target
引用丢失,但是,第二次,我们缓存了响应,并立即回调到
Target
。这是一个活动->
Picasso.with(MainActivity.this)
您对此有解决方案吗?我的目标不是也很强大吗?如果不是,为什么?您的目标正在被垃圾收集,因为它是在方法中声明的局部变量。毕加索在另一个线程上加载drawable,您的局部变量被垃圾收集。将目标设置为实例变量将使其保留在内存中
MenuItemIconLoader
也是在同一方法中声明的局部变量。什么让它留在记忆中?为什么不收集垃圾?另外,为什么要使用
WeakReference
Target
是一个实例变量,这就是它保存在内存中的原因。我对
菜单项使用了
WeakReference
,因为我不确定它是否会保留
活动
,并可能导致内存泄漏。您可能不需要它。我是指将
MenuItemIconLoader
保留在内存中的内容。它也不是实例变量。它也是一个局部变量。