更加简洁且通用的ViewHolder写法

本文介绍了一种简化ViewHolder使用的方法,通过自定义ViewHolder工具类减少代码重复性,提高ListView和GridView性能。并详细解释了ViewHolder中get()方法的实现原理。


一、进一步简化你的 ViewHolder

目前在写项目的过程中,虽然 RecyclerView 已经出来很久了,但是仍有一部分朋友(包括我)再继续使用着 ListView,GridView . 
在使用的过程中,使用 ViewHolder 进行 ListView,GridView 的优化,是必不可少的,所以,我们每次在 getView 的方法中 都会去利用 ViewHolder 去进行 View 的复用。 
写法如下:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
        //ViewHolder
        ViewHolder viewHolder=null;
        //绘制每一项的时候先判断convertView是否为空,不为空,则else里面去复用,为空,则重新赋予item布局
        if (convertView == null) {
            //new出ViewHolder ,初始化布局文件
            viewHolder = new ViewHolder();

            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_test, null);

            viewHolder.tv_name= (TextView) convertView.findViewById(R.id.tv_name);

            //调用convertView的setTag方法,将viewHolder放入进去,用于下次复用
            convertView.setTag(viewHolder);
        } else {
            //复用已经存在的item的项
            viewHolder = (ViewHolder) convertView.getTag();
        }
        viewHolder.tv_name.setText(listBeans.get(position));
        return convertView;
    }

    class ViewHolder {
        TextView tv_name;
    }

这样的写法使得我们即便 Item 里面只有一两个控件,也都要去写一个 ViewHolder ,这也就算了,如果 Item 里面控件多了,我们就需要花费一定的时间先去创建 ViewHolder。 不知道有没有朋友想过寻找一些简化的方法。

我这里整理出一个简化使用 ViewHolder 的方法,相对于创建上面的 getTag() setTag(), 要优雅许多,并且在使用上,也避免了做一些重复性的工作。 
这里直接写为一个工具类:

public class ViewHolder {
    public static <t extends="" view=""> T get(View view, int id) {
        SparseArray<view> viewHolder = (SparseArray<view>) view.getTag();
        if (viewHolder == null) {
            viewHolder = new SparseArray<view>();
            view.setTag(viewHolder);
        }
        View childView = viewHolder.get(id);
        if (childView == null) {
            childView = view.findViewById(id);
            viewHolder.put(id, childView);
        }
        return (T) childView;
    }
}

然后在 getView 中使用方式为:

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

        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_test, null);
        }

        TextView tv_name = ViewHolder.get(convertView , R.id.tv_name);

        tv_name.setText(listBeans.get(position));

        return convertView;
    }

二、学习ViewHolder中get()方法的思路

ViewHolder中的get()方法里,使用了一个类:SparseArray,该类是android.util包中提供的一个类,主要用于代替Map<Integer, Object>,SparseArray内部通过键值对存放数据,其中键是整型数据,SparseArray的内部采用折半查找的思想,和普通的Map<Integer, Object>相比,拥有更好的性能,下面分析一下ViewHolder.get()方法:
public class ViewHolder {  
  
    @SuppressWarnings("unchecked")  
    public static <T extends View> T get(View view, int id){  
        //从convertView中获取tag,SparseArray里面存放View和View对应的id,通过View的id来查找View  
        SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();  
        //如果获取不到,则创建SparseArray对象,并将SparseArray对象放入convertView的tag中  
        if(viewHolder == null){  
            viewHolder = new SparseArray<View>();  
            view.setTag(viewHolder);  
        }  
        //通过View的id来获取View,在SparseArray的内部使用的折半查找  
        View childView = viewHolder.get(id);  
        //如果在SparseArray中没有对应的id,则通过findViewById来获取View  
        if(childView == null){  
            childView = view.findViewById(id);  
            //将获取到的View和与View对应的id存入SparseArray中  
            viewHolder.put(id, childView);  
        }  
        //返回值是方法的第二个参数id对应的View  
        return (T) childView;  
    }  
}  




为了使用 ViewHolder 写法优化这段代码逻辑,首先需要定义一个 ViewHolder 类来缓存视图中的子控件。以下是优化后的代码: ```java // 定义 ViewHolder 类 static class MediaViewHolder { // 这里可以根据布局文件中的控件来定义成员变量 // 假设 ascf_media_image_viewer_item 和 ascf_media_video_viewer_item 布局中有一些共同的控件,如一个显示标题的 TextView TextView titleTextView; MediaViewHolder(View view) { // 初始化控件 titleTextView = view.findViewById(R.id.title_text_view); // 假设布局中有一个 id 为 title_text_view 的 TextView } } String type = mediaSource.getType(); int viewType = getViewType(type); View view = getCachedView(viewType); MediaViewHolder holder; if (view == null) { if (PreviewMediaConstant.PREMEDIA_IMAGE.equalsIgnoreCase(type)) { view = LayoutInflater.from(context).inflate(R.layout.ascf_media_image_viewer_item, container, false); } else if (PreviewMediaConstant.PREMEDIA_VIDEO.equalsIgnoreCase(type)) { view = LayoutInflater.from(context).inflate(R.layout.ascf_media_video_viewer_item, container, false); } else { AscfTrace.e("MediaSource", "Unknown media type at position " + position); return new View(context); } // 创建 ViewHolder 实例 holder = new MediaViewHolder(view); // 将 ViewHolder 存储在视图的 Tag 中 view.setTag(holder); } else { // 从视图的 Tag 中获取已缓存的 ViewHolder holder = (MediaViewHolder) view.getTag(); } // 使用 ViewHolder 中的控件进行操作 // 假设 mediaSource 有一个 getTitle 方法 holder.titleTextView.setText(mediaSource.getTitle()); ``` ### 代码解释: 1. **定义 ViewHolder 类**:创建了一个 `MediaViewHolder` 类,用于缓存视图中的子控件。在构造函数中,通过 `findViewById` 方法初始化这些控件。 2. **检查视图是否为空**:如果视图为空,根据媒体类型充气相应的布局文件,并创建一个新的 `MediaViewHolder` 实例,将其存储在视图的 Tag 中。 3. **视图不为空**:如果视图不为空,从视图的 Tag 中获取已缓存的 `MediaViewHolder` 实例。 4. **使用 ViewHolder 操作控件**:通过 `MediaViewHolder` 实例操作缓存的控件,避免了重复调用 `findViewById` 方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值