摆脱ViewHolder,一种无需ViewHolder的高效ListView写法

来源:互联网 发布:淘宝怎样发布二手宝贝? 编辑:程序博客网 时间:2024/06/05 05:06

使用ListView来展示列表数据,基本上是每个Android猿必须掌握的技能,而ListView的基本优化技巧,基本也烂大街了,无非是复用convertView对象还有使用ViewHolder来缓存Item中各个子View的引用。
最近看到一篇文章,描述了一种非主流的高效的ListView写法,无需创建ViewHolder类和holder对象,不用setTag()和getTag(),感觉效率更高。

原文见

http://www.bignerdranch.com/blog/customizing-android-listview-rows-subclassing/

原文作者通过创建RelativeLayout子类(这里ListView各item的布局的根布局是RelativeLayout,根据实际情况而定)来封装所有自定义操作,如findViewById各个子View,填充数据等。

原文作者不喜欢ViewHolder的原因

  • ViewHolder模式会在Adapter的getView(…)方法中干了太多事。
  • ViewHolder类太过公式化,创建和设置holder让人累觉不爱。
  • View.getTag()得到的对象需要强转为正确的holder类型,坑(查不到kludgy的翻译,只能这样了,坑)。
  • 因为adapter/holder需要知道代表列表各项的view的内部结构,有违封装性。

无需ViewHolder的写法

出于复用布局的目的,原文作者定义了两个布局文件和使用merge标签来描述item的view,不过我觉得这不是这个写法的重点,所以改了一下,只是简单地定义了一个布局文件来描述item view。
先看看Demo截图
截图

完整代码猛戳 这里

1. 定义一个Item类来定义列表数据。

1
2
3
4
5
6
public class Item {
private int imgId;
private String title;
private String content;
...getter...setter...
}

2. 定义item view的布局(见item_view.xml)。
3. 自定义ItemView类,继承步骤2中定义的布局的根布局(这里是RelativeLayout),实现参数为(Context context, AttributeSet attrs)(Context context, AttributeSet attrs, int defStyle)的构造方法,并添加用于获取子View对象引用、获取ItemView对象和填充数据的方法,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class ItemView extends RelativeLayout {
private ImageView ivImage;
private TextView tvTitle;
private TextView tvContent;
public ItemView(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 获取ItemView
public static ItemView newInstance(ViewGroup parent) {
ItemView view = (ItemView) LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view,parent,false);
view.setupChildren();
return view;
}
// 获取子View对象的引用
private void setupChildren() {
ivImage = (ImageView) findViewById(R.id.iv_image);
tvTitle = (TextView) findViewById(R.id.tv_title);
tvContent = (TextView) findViewById(R.id.tv_content);
}
// 填充数据
public void populateData(Item item) {
ivImage.setImageResource(item.getImgId());
tvTitle.setText(item.getTitle());
tvContent.setText(item.getContent());
}
}

4. 创建ListView用的adapter类(ItemAdapter)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ItemAdapter extends ArrayAdapter<Item> {
public ItemAdapter(Context context, List<Item> objects) {
super(context, 0, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ItemView view = (ItemView) convertView;
if (view == null) {
view = ItemView.newInstance(parent);
}
view.populateData(getItem(position));
return view;
}
}

5. (很重要,我经常会忘了这步)把item view中布局(item_view.xml)的根标签,改为ItemView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<com.licheetec.noholderlistviewdemo.ItemView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="64dp"
android:padding="8dp">
<ImageView
android:id="@+id/iv_image"
.../>
<TextView
android:id="@+id/tv_title"
.../>
<TextView
android:id="@+id/tv_content"
.../>
</com.licheetec.noholderlistviewdemo.ItemView>

6. 剩下的就是在activity获取和设置ListView了。

总结这种写法的优势

  • Adapter的代码会变得极其简洁
  • 很容易通过代码和xml文件来创建ItemView
  • 以后有需要更改,只需要对布局文件和ItemView进行动刀即可
  • 整个过程无需创建额外的holder类和对象

好了,基本就这样,转述可能跟原文有些出入,有能力的话可以看原文怎么说明的。

0 0
原创粉丝点击