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

使用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
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值