Android 一起来封装一个简单易用的Adapter

前言

还记得我初学 Android 没多久又需要用到 ListView 的时候还不会写 Adapter,结果我居然硬生生的用 TableLayout 和 LinearLayout 把 ListView 给替代了。现在回过神了想想,我当时还真是厉害啊,在另一种意义上尴尬

其实要会写 Adapter,乃至于封装一个能提高生产效率的 Adapter,是离不开对 ListView、GridView 到 RecyclerView 等一系列视图的 Item 复用机制的理解和掌握的。不熟悉的朋友们请至 http://blog.csdn.net/lmj623565791/article/details/24333277 学习


正文

我还依稀记得最早是这么写一个 Adapter 的(为什么是依稀疑问

BaseAdapter adapter = new BaseAdapter() {
        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public String getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            convertView = inflater.inflate(layoutId, parent, false);
            
            TextView textView = convertView.findViewById(textViewId);
            Button button = convertView.findViewById(buttonId);
            
            String data = getItem(position);
            textView.setText(data);
            button.setOnClickListener(onClickListener);
            
            return convertView;
        }
    };


后来我才意识到,这样子写在滑动的时候会很频繁的去 inflate,开销很大,是不合格的写法,于是改成了下面这样:

BaseAdapter adapter = new BaseAdapter() {
        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public String getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            if (convertView == null) {
                convertView = inflater.inflate(layoutId, parent, false);

                viewHolder = new ViewHolder();
                viewHolder.textView = convertView.findViewById(textViewId);
                viewHolder.button = convertView.findViewById(buttonId);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            String data = getItem(position);
            viewHolder.textView.setText(data);
            viewHolder.button.setOnClickListener(onButtonClickeListener);

            convertView.setTag(viewHolder);
            convertView.setOnClickListener(onConvertViewClickedListener);
            return convertView;
        }
    };

class ViewHolder {
        TextView textView;
        Button button;
    }

这样子写基本上没有遇到遇到太大问题,也就最初容易遇到因为 item 复用导致的各种混乱,在理解了复用机制之后其实很容易避免。我也曾经见到过将 convertView 传入 ViewHolder 中,在 viewHolder 里去实现大部分逻辑的写法,这里就不一一赘述了。


现在来想一下,写一个 Adapter,我们要做些什么?大致有这些吧:

1.实现 BaseAdapter 的 getCount、getItem、getItemId 这三个方法;

2.新建一个 ViewHolder 类,当中要包含一个 item 中需要操作的所有 view;

3.在 BaseAdapter 的 getView 方法中实现 item 复用,处理好 viewHolder 与 convertView 的关系;

4.处理 item,设置数据至 view ,添加监听等。


看上去也不太多嘛,就4步。但是实际上多写几次就很难受了,要是项目中 ListView、GridView 特别多,估计能把一只猿活活写吐了!

因为1、2、3的代码基本上每次都差不多,不同之处大多都在于4。一个复杂的 ListView 写下来感觉腰酸背痛手抽筋,简单的则感觉不会再爱了,毕竟花在写4的时间上好像远远小于123的重复工作。久而久之,我一听到要做 ListView 就会这样 --> 惊恐


然后在一个月黑风高的夜晚,我意外得到了一件神器

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;
    }
}


从代码上看,这个 ViewHolder 类会往传入的 view 中存一系列 id-view 的键值对作为 tag,代码并不复杂,相信大家都看得懂。

我们再看看这神器到底能干什么

BaseAdapter adapter = new BaseAdapter() {
        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public String getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = inflater.inflate(layoutId, parent, false);
            }
            
            TextView textView = ViewHolder.get(convertView, textViewId);
            Button button = ViewHolder.get(convertView, buttonId);
            
            String data = getItem(position);
            textView.setText(data);
            button.setOnClickListener(onButtonClickListener);
            
            return convertView;
        }
    };

完全不用专门写 ViewHolder 类了有木有,对于我这样的懒人简直就是福音得意

以上就是神器 ViewHolder 的功效。


难道这是全部了吗?作为一个懒惰的程序猿,相信大家肯定和我一样不满足于此。放眼上述的代码,不管是哪个 adapter 都写了几个重复的方法,那些方法要怎么干掉呢?

其实,一个简单的基类加上对泛型的支持就可以了

public abstract class ListAdapter<T> extends BaseAdapter {

	protected abstract void setItem(View convertView, T data, int position);
	
	protected List<T> mData;
	protected Context mContext;
	protected LayoutInflater mInflater;
	protected int mLayoutRes;

	public ListAdapter(Context context, List<T> data, int layoutRes) {
		this.mData = data;
		this.mContext = context;
		this.mLayoutRes = layoutRes;
		
		this.mInflater = LayoutInflater.from(mContext);
	}

	/**
	 * 刷新 adapter
	 * 考虑到可能会发生数据完全改变的情况,故提供此方法
	 * @param data
     */
	public void refresh(List<T> data) {
		try {
			this.mData = data;
	        notifyDataSetChanged();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public int getCount() {
		return mData.size();
	}

	@Override
	public T getItem(int position) {
		return mData.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		try {
			if (convertView == null) {
				convertView = mInflater.inflate(mLayoutRes, null);
			}

			setItem(convertView, getItem(position), position);

		} catch (Exception e) {
			e.printStackTrace();
		}
		return convertView;
	}

	public <V extends View> V getChildView(View view, int id) {
		return ViewHolder.get(view, id);
	}
}

封装到此结束,让我们看看是怎么使用的吧

BaseAdapter adapter = new ListAdapter<String>(MainActivity.this, list, layoutId) {
        @Override
        protected void setItem(View convertView, String data, int position) {
            TextView textView = getChildView(convertView, textViewId);
            textView.setText(data);
            
            getChildView(convertView, buttonId)
                    .setOnClickListener(onButtonClickListener);
        }
    };


没仔细看前面代码的围观群众:WTF?这就完了?

没错,传入上下文、数据集合,还有布局文件,重写一个 setItem 方法就完了。相比最初的 adapter,我们只需要专注于布局、数据和视图的绑定,已经事件的监听,不需要重写相同的代码,不需要写累赘的 ViewHolder 类,不需要写八股文一般的 Item 复用代码。


虽然并不完美,也还没做到极简,但一个简单好用的 Adapter 已经初步成型了。谢谢各位看官赏脸看到现在。

下一篇文章打算在这次的 ListAdapter 的基础上封装 RecyclerView 的 Adapter,并且提供对 ItemType 的支持方式的参考思路。

项目源码:https://github.com/neverwoodsS/zy-open


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值