关于ListView中的ViewHolder模式实现优化

时间:2013-04-16 08:26:41

标签: android performance listview

因此,众所周知的ViewHolder模式通常看起来像(ListAdapter):

    ...

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

        final Album album = albums.get(position);

        ViewHolder viewHolder = null;
        if (convertView==null){
            convertView = inflater.inflate(R.layout.albums_list_item, null);

            final ImageView albumImage = (ImageView) convertView.findViewById(R.id.album_icon);

            final TextView txtTitle = (TextView) convertView.findViewById(R.id.album_title);

            final TextView txtDescription = (TextView) convertView.findViewById(R.id.album_copyright);

            viewHolder = new ViewHolder();
            viewHolder.albumImage = albumImage;
            viewHolder.txtTitle = txtTitle;
            viewHolder.txtDescription = txtDescription;
            convertView.setTag(viewHolder);
        }
        else
            viewHolder = (ViewHolder)convertView.getTag();

        viewHolder.txtTitle.setText(album.getTitle(locale));
        viewHolder.txtDescription.setText(album.getCopyrightInfo(locale));
        ...
        return convertView;
    }

而ViewHolder类通常如下所示:

static class ViewHolder{
    public ImageView previewImage;
    public TextView txtTitle;
    public TextView txtDescription;
}

我的问题是关于ViewHolder的实现 1)为什么不使用构造函数而不是初始化每个字段?
2)为什么它使用默认访问类型而不是受保护(实际上它必须是私有的,但由于JIT创建的静态访问器,这会影响性能)?好吧,我猜它只是关于继承。
那么为什么以下模式不是更好(不包括“受保护与默认”访问类型):

protected static class ViewHolder{
    public final ImageView previewImage;
    public final TextView txtTitle;
    public final TextView txtDescription;

    public ViewHolder (final ImageView previewImage,  final TextView txtTitle, final TextView txtDescription){
        this.previewImage = previewImage;
        this.txtTitle = txtTitle;
        this.txtDescription = txtDescription;
    }
}

并且ListAdapter中唯一的变化是:

...
final TextView txtDescription = (TextView) convertView.findViewById(R.id.album_copyright);
viewHolder = new ViewHolder(albumImage, txtTitle, txtDescription);
convertView.setTag(viewHolder);
...

无论如何它必须调用构造函数。这只是品味问题吗?或者这个版本是以某种方式变慢还是以某种方式影响性能?

3 个答案:

答案 0 :(得分:5)

我使用的方法与你的方法非常相似,但我更进一步,因为ViewHolder对于适配器类是私有的,我通过将视图传递给它的构造函数并设置它来将它紧密地耦合到类中。那里的价值,例如

    private class ViewHolder
    {
      protected final ImageView image;
      protected final TextView  title;
      protected final TextView  status;

      public ViewHolder( final View root )
      {
         image = (ImageView) root.findViewById( R.id.artist_image );
         title = (TextView) root.findViewById( R.id.artist_title );
         status = (TextView) root.findViewById( R.id.artist_status );
      }
   }

并在getView(...)

 View row = convertView;

  if ( null == row || null == row.getTag() )
  {
     row = inflater.inflate( R.layout.adapter_artists, null );
     holder = new ViewHolder( row );
     row.setTag( holder );
  }
  else
  {
     holder = (ViewHolder) row.getTag();
  }

我喜欢这样做,因为它使我的适配器代码在getView(...)中更简单,并且具有最终变量的好处。我可能会获得一个小的速度提升,使其受到保护,但我发现即使有大量的列表,性能也足够了。

答案 1 :(得分:2)

我认为这只是味道的问题。至于我,它甚至看起来比标准的更好。此外,由于使用了最终变量,您的版本可能会更快。

答案 2 :(得分:1)

在我看来,这是最好的方式,但有些东西我会改变你的代码。您在ViewHolder中有一个构造函数,您可以在其中设置视图,但我可以看到您没有在代码中使用它。我会使用它或只是删除它。还有一个例子,实际上有更好的方法来获得相同的效果,但它只能在Android 4+上运行:

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

    ImageView mIcon;
    TextView mName;
    if (convertView == null) {
        convertView = LayoutInflater.from(context)
          .inflate(R.layout.my_contact_listing, parent, false);
        mIcon = (ImageView) convertView.findViewById(R.id.contact_icon);
        mName = (TextView) convertView.findViewById(R.id.contact_name);
        convertView.setTag(R.id.contact_icon, mIcon);
        convertView.setTag(R.id.contact_name, mName);
    } else {
        mIcon = (ImageView) convertView.getTag(R.id.contact_icon);
        mName = (TextView) convertView.getTag(R.id.contact_name);
    }

    Contact mContact = getItem(position);
    mName.setText(mContact.getName());
    mIcon.setImageResource(mContact.getIcon());

    return convertView;
}