ListView中的ViewHolder模式及其作用

ViewHolder是ListView,GridView中常用的优化方案。以往看到很多文章,将缓存View与ViewHodler的使用放在一起来讲,但实际使用ViewHolder是有明确的目的和作用的。

先来看一下最基本,但也是不正确的ListView使用方法:

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

    LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View mView = inflater.inflate(R.layout.list_item, parent, false);

    TextView textView = (TextView) mView.findViewById(R.id.item_text);
    ImageView imageView = (ImageView) mView.findViewById(R.id.item_image);

    textView.setText(R.string.app_name);
    imageView.setImageResource(R.drawable.ic_launcher);

    return mView;
}

这时IDE也会提示我们这样的代码是不正确的:

Unconditional layout inflation from view adapter: Should use View Holder pattern (use recycled view passed into this method as the second parameter) for smoother scrolling

实际这个改进的建议包含两方面
1.我们应当通过缓存机制,减少无条件的载入xml,降低内存使用
2.使用ViewHolder模式

先来看一下不用ViewHolder,且引入缓存机制的方法:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.list_item, parent, false);
    }

    TextView textView = (TextView) convertView.findViewById(R.id.item_text);
    ImageView imageView = (ImageView) convertView.findViewById(R.id.item_image);

    textView.setText(R.string.app_name);
    imageView.setImageResource(R.drawable.ic_launcher);

    return convertView;
}

在这里我们通过判断getView中的convertView参数,来决定是否载入xml生成view。当系统传递来null值时,则代表没有缓存的view可供使用,我们需要通过xml布局来生成新的view;若系统传递进来已有view对象,那么我们就不需要重新生成了,直接复用即可。
但在这样的代码逻辑下,也存在一个影响效率的因素:
无论是否使用了缓存的View,我们都需要重新通过findViewById来查找布局中的某一子元素,以便进一步操作,这还是比较印象效率的。因此我们引入了ViewHolder模式,来减少findViewById方法的调用,提高子View获取的效率:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    if (convertView == null) {
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.list_item, parent, false);
        holder = new ViewHolder();
        holder.textView = (TextView) convertView.findViewById(R.id.item_text);
        holder.imageView = (ImageView) convertView.findViewById(R.id.item_image);

        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    holder.textView.setText(R.string.app_name);
    holder.imageView.setImageResource(R.drawable.ic_launcher);

    return convertView;
}

看起来代码变多了,但实际上是有好处的:
View的setTag方法可以允许我们传入一个Object对象,来存储相应信息。因此我们生命了ViewHolder类,其中保存了TextView与ImageView的引用,我们将其设置到View的Tag中后,若系统传入非空的convertView时,我们便可以通过getTag直接过去到两个子VIiw的引用,而免去了调用findViewById。通过这样优化以后,我们发现只有在convertView为null值时需要调用findVIewById,这大大节省了getView内部的处理速度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值