简体   繁体   English

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

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

I created a custom listview layout with images which are loaded from web like this: 我创建了一个自定义列表视图布局,其中包含从Web加载的图像,如下所示:

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

It works fine when scrolling down. 向下滚动时工作正常。 However, when you scroll down, the previous items go out of screen then destroyed. 但是,当您向下滚动时,之前的项目将从屏幕中移出然后销毁。 When you try to scroll up again, it gets loaded again (from cache, faster but not instant) which causes a delay and it is not fluent as it should be. 当你再次尝试向上滚动时,它会再次被加载(从缓存,更快但不是即时),这会导致延迟并且它不应该流畅。

1.Is there an example of how to do this properly? 1.有一个如何正确做到这一点的例子吗?
2.Is there a way to prevent listview items being destroyed when they are out of screen? 2.有没有办法防止列表视图项目在屏幕外被销毁?
3.If so, will it cause problems to keep too many items? 3.如果是这样,是否会因为保留太多物品而导致问题?

Bellow is my code: 贝娄是我的代码:

MenuAdapter: 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: 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;
    }
}

MainActivity: 主要活动:

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: 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. Is there an example of how to do this properly? 1.有没有一个如何正确做到这一点的例子?

Your code looks pretty close to perfect. 您的代码看起来非常接近完美。 The Adapter's getView method is usually the critical path to optimize. Adapter的getView方法通常是优化的关键途径。 Compare for example Picasso's own example SampleListDetailAdapter.java . 比较Picasso自己的示例SampleListDetailAdapter.java The important points it (as well as your code) does 重要的一点(以及你的代码)

  • check for & re-use already inflated views, inflation is expensive. 检查和重新使用已经膨胀的观点,通货膨胀是昂贵的。
  • use ViewHolder so you don't have to call findViewById every time. 使用ViewHolder因此您不必每次都调用findViewById Not terribly expensive on simple views. 在简单的视图上并不是非常昂贵。 Also cached afaik. 也缓存了afaik。
  • Picasso.with(context).load(url)... each time you need to display an image. Picasso.with(context).load(url)...每次需要显示图像时。 This should finish instantly but still use caches and other magic. 这应该立即完成,但仍然使用缓存和其他魔法。

There are some minor optimizations you can add, but I doubt that there are noticeable or even measurable changes: 您可以添加一些小的优化,但我怀疑有明显的甚至可衡量的变化:

pure style change: use BaseAdapter#getItem(position) . 纯样式更改:使用BaseAdapter#getItem(position) This method exists for you only. 此方法仅适用于您。 The framework doesn't use it. 框架不使用它。

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

Use a sane id method 使用理智的id方法

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

is equivalent to 相当于

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

but now infinitely faster. 但现在无限快。 indexOf(Object) scales really badly with the number of objects. indexOf(Object)与对象数量的关系非常严重。

Cache objects that don't change: 缓存不更改的对象:

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. Is there a way to prevent listview items being destroyed when they are out of screen? 2.有没有办法阻止列表视图项目在屏幕外被销毁?

No(*). 没有(*)。

..(*) well you can essentially cache the result of getView eg in LruCache(position, View) or LruCache(MyMenuItem, View) , then don't touch the convertView - they need to remain unconverted or you would kill those views in your cache. ..(*)你可以基本上缓存getView的结果,例如在LruCache(position, View)LruCache(MyMenuItem, View) ,然后不要触摸convertView - 它们需要保持未转换状态,否则你会杀掉那些视图你的缓存。 Also

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

seemed to be required because the standard adapter using code assumes that views it removes from visibility are gone. 似乎是必需的,因为使用代码的标准适配器假定它从可见性中删除的视图消失了。 They are not and messing with them messes with your cache and caused weird display problems for me. 他们不是和他们搞乱你的缓存混乱,并为我造成了奇怪的显示问题。

3. If so, will it cause problems to keep too many items? 3.如果是这样,是否会因为保留太多物品而导致问题?

Yes. 是。 This behavior is not intendend / expected. 此行为不是意图/预期。 There is also more or less nothing you gain. 你或多或少都没有获得。 You might be able to save you the call to holder.tvMenuHeader.setText() . 您可以保存对holder.tvMenuHeader.setText()的调用。 Likewise the one to Picasso but both of them should complete instantly. 同样是Picasso的一个,但他们两个都应该立即完成。 Picasso should have your image cached already. Picasso应该已经缓存了你的图像。 By caching all Views you essentially add another cache that also contains all the images. 通过缓存所有Views您实际上添加了另一个包含所有图像的缓存。 I would rather check that the picasso cache works as intended and holds most items. 我宁愿检查毕加索缓存是否按预期工作并保存大部分项目。 The only reason you may want to do it with view caching is for cases that require complicated setup of the view, so it becomes worth caching the completely constructed view rather than just some content parts. 您可能希望使用视图缓存执行此操作的唯一原因是需要复杂设置视图的情况,因此值得缓存完全构造的视图而不仅仅是某些内容部分。

Profile 轮廓

Profiling can actually tell you where you can / need / should improve. 分析实际上可以告诉您可以/需要/应该改进的地方。 The first to look at IMO is traceview. 第一个看IMO的是traceview。 You'll see if code blocks the main thread which results in choppy list scrolling. 您将看到代码是否阻止主线程导致乱序列表滚动。 If you're doing complicated views and you see that the draw methods are executed most of the time, profile them as well. 如果您正在执行复杂的视图,并且您发现绘制方法大多数时间都在执行,那么也要对它们进行概要分析。

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

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