繁体   English   中英

如何使用Picasso库正确实现带图像的自定义列表视图?

[英]How to correctly implement a custom listview with images using Picasso library?

我创建了一个自定义列表视图布局,其中包含从Web加载的图像,如下所示:

http://i.stack.imgur.com/l8ZOc.png

向下滚动时工作正常。 但是,当您向下滚动时,之前的项目将从屏幕中移出然后销毁。 当你再次尝试向上滚动时,它会再次被加载(从缓存,更快但不是即时),这会导致延迟并且它不应该流畅。

1.有一个如何正确做到这一点的例子吗?
2.有没有办法防止列表视图项目在屏幕外被销毁?
3.如果是这样,是否会因为保留太多物品而导致问题?

贝娄是我的代码:

MenuAdapter:

public class MenuAdapter extends BaseAdapter{

    Context context;
    List<MyMenuItem> menuItems;

    MenuAdapter(Context context, List<MyMenuItem> menuItems) {
        this.context = context;
        this.menuItems = menuItems;
    }

    @Override
    public int getCount() {
        return menuItems.size();
    }

    @Override
    public Object getItem(int position) {
        return menuItems.get(position);
    }

    @Override
    public long getItemId(int position) {
        return menuItems.indexOf(getItem(position));
    }

    private class ViewHolder {
        ImageView ivMenu;
        TextView tvMenuHeader;
    }



    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder holder = null;

        LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.menu_item, null);
            holder = new ViewHolder();

            holder.tvMenuHeader = (TextView) convertView.findViewById(R.id.tvMenuHeader);
            holder.ivMenu = (ImageView) convertView.findViewById(R.id.ivMenuItem);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        MyMenuItem row_pos = menuItems.get(position);

        Picasso.with(context)
                .load(row_pos.getItem_image_url())
                .into(holder.ivMenu);

        holder.tvMenuHeader.setText(row_pos.getItem_header());

        Log.e("Test", "headers:" + row_pos.getItem_header());
        return convertView;
    }

}

MyMenuItem:

public class MyMenuItem {

    private String item_header;
    private String item_image_url;

    public MyMenuItem(String item_header, String item_image_url){
        this.item_header=item_header;
        this.item_image_url=item_image_url;
    }

    public String getItem_header(){
        return item_header;
    }

    public void setItem_header(String item_header){
        this.item_header=item_header;
    }

    public String getItem_image_url(){
        return item_image_url;
    }

    public void setItem_image_url(String item_image_url){
        this.item_image_url=item_image_url;
    }
}

主要活动:

public class MyActivity extends Activity implements AdapterView.OnItemClickListener {

    List<MyMenuItem> menuItems;
    ListView myListView;
    JSONArray jsonArray;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        Bundle extras = getIntent().getExtras();

        if(extras!=null){
            try{
                jsonArray = new JSONArray(extras.getString("Data"));
            }catch (Exception e){
                e.printStackTrace();
            }
        }


        menuItems = new ArrayList<MyMenuItem>();


        for (int i = 0; i < jsonArray.length(); i++) {
            try {
                MyMenuItem item = new MyMenuItem(jsonArray.getJSONObject(i).getString("title"), jsonArray.getJSONObject(i).getString("imageURL"));
                menuItems.add(item);
            }catch (Exception e){
                e.printStackTrace();
            }

        }

        myListView = (ListView) findViewById(R.id.list);
        MenuAdapter adapter = new MenuAdapter(this, menuItems);
        myListView.setAdapter(adapter);
        myListView.setOnItemClickListener(this);
    }
}

MenuItem.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <ImageView
        android:id="@+id/ivMenuItem"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scaleType="center"
        android:src="@drawable/em" />

    <TextView
        android:id="@+id/tvMenuHeader"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#55000000"
        android:paddingBottom="15dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="15dp"
        android:textColor="@android:color/white"
        android:layout_gravity="left|top"
        android:layout_alignBottom="@+id/ivMenuItem"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true" />
</RelativeLayout>

1.有没有一个如何正确做到这一点的例子?

您的代码看起来非常接近完美。 Adapter的getView方法通常是优化的关键途径。 比较Picasso自己的示例SampleListDetailAdapter.java 重要的一点(以及你的代码)

  • 检查和重新使用已经膨胀的观点,通货膨胀是昂贵的。
  • 使用ViewHolder因此您不必每次都调用findViewById 在简单的视图上并不是非常昂贵。 也缓存了afaik。
  • Picasso.with(context).load(url)...每次需要显示图像时。 这应该立即完成,但仍然使用缓存和其他魔法。

您可以添加一些小的优化,但我怀疑有明显的甚至可衡量的变化:

纯样式更改:使用BaseAdapter#getItem(position) 此方法仅适用于您。 框架不使用它。

   @Override
   public MyMenuItem getItem(int position) { // << subclasses can use subtypes in overridden methods!
       return menuItems.get(position);
   }

   @Override
   public View getView(int position, View convertView, ViewGroup parent) {
       ...
       MyMenuItem row_pos = getItem(position);

使用理智的id方法

@Override
public long getItemId(int position) {
    return menuItems.indexOf(getItem(position));
}

相当于

@Override
public long getItemId(int position) {
    return position;
}

但现在无限快。 indexOf(Object)与对象数量的关系非常严重。

缓存不更改的对象:

MenuAdapter(Context context, List<MyMenuItem> menuItems) {
    this.mLayoutInflater = LayoutInflater.from(content);
    this.mPicasso = Picasso.with(context);
}
..
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.menu_item, null);
    ...
    mPicasso
            .load(row_pos.getItem_image_url())
            .into(holder.ivMenu);

2.有没有办法阻止列表视图项目在屏幕外被销毁?

没有(*)。

..(*)你可以基本上缓存getView的结果,例如在LruCache(position, View)LruCache(MyMenuItem, View) ,然后不要触摸convertView - 它们需要保持未转换状态,否则你会杀掉那些视图你的缓存。

@Override
public int getItemViewType(int position) {
    return Adapter.IGNORE_ITEM_VIEW_TYPE;
}

似乎是必需的,因为使用代码的标准适配器假定它从可见性中删除的视图消失了。 他们不是和他们搞乱你的缓存混乱,并为我造成了奇怪的显示问题。

3.如果是这样,是否会因为保留太多物品而导致问题?

是。 此行为不是意图/预期。 你或多或少都没有获得。 您可以保存对holder.tvMenuHeader.setText()的调用。 同样是Picasso的一个,但他们两个都应该立即完成。 Picasso应该已经缓存了你的图像。 通过缓存所有Views您实际上添加了另一个包含所有图像的缓存。 我宁愿检查毕加索缓存是否按预期工作并保存大部分项目。 您可能希望使用视图缓存执行此操作的唯一原因是需要复杂设置视图的情况,因此值得缓存完全构造的视图而不仅仅是某些内容部分。

轮廓

分析实际上可以告诉您可以/需要/应该改进的地方。 第一个看IMO的是traceview。 您将看到代码是否阻止主线程导致乱序列表滚动。 如果您正在执行复杂的视图,并且您发现绘制方法大多数时间都在执行,那么也要对它们进行概要分析。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM