使用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);
}
public
static ItemView
newInstance(ViewGroup parent) {
ItemView view = (ItemView) LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view,parent,
false);
view.setupChildren();
return 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类和对象
好了,基本就这样,转述可能跟原文有些出入,有能力的话可以看原文怎么说明的。