Android之ListView的高级封装!
16lz
2021-01-23
在任何项目中ListView无疑是用得最多的Android原生控件之一,ListView的使用方法、复用ConvertView、以及ViewHolder的使用这里就不再赘述了。
本文想要谈谈的是,当项目中反复使用到ListView时,如何对ListView进行封装,简化重复代码,提高效率。
class MyAdapter extends BaseAdapter {@Overridepublic int getCount() {return data.size();}@Overridepublic Object getItem(int position) {return data.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if(convertView == null){holder = new ViewHolder();// 1.加载布局文件View view = View.inflate(context, R.layout.listviewitem, null);// 2.初始化控件holder.textView = view.findViewById(R.id.textView1);// 3.打一个tag标记view.setTag(holder);}else{holder = (ViewHolder) convertView.getTag();} // 4.设置数据刷新内容holder.textView.setText("设置内容");return convertView;} } static class ViewHolder { TextView textView; }
但是如何封装呢?封装BaseAdapter最需要注意的方法就是getView()方法,但是我们可以看到,它一般的就是4个目的:
- 加载布局文件
- 初始化控件
- 打一个tag标记
- 设置ListView的item数据
具体封装好的额两个MyBaseHolder和MyBaseAdapter直接先贴出来如下:
public abstract class MyBaseHolder<T> {// 一个item的根布局private View mRootView;// 一个item的数据private T data;// 当new这个对象时, 就会加载布局, 初始化控件,设置tagpublic MyBaseHolder() {mRootView = initView();// 3. 打一个标记tagmRootView.setTag(this);}// 1. 加载布局文件// 2. 初始化控件 findViewByIdpublic abstract View initView();// 返回item的布局对象public View getRootView() {return mRootView;}// 设置当前item的数据public void setData(T data) {this.data = data;refreshView(data);}// 获取当前item的数据public T getData() {return data;}// 4. 根据数据来刷新界面public abstract void refreshView(T data);}
public abstract class MyBaseAdapter<T> extends BaseAdapter {private ArrayList<T> data;public MyBaseAdapter(ArrayList<T> data) {this.data = data;}@Overridepublic int getCount() {return data.size();}@Overridepublic T getItem(int position) {return data.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {MyBaseHolder<T> baseHolder;if(convertView == null){// 1. 加载布局文件// 2. 初始化控件 findViewById// 3. 打一个标记tagbaseHolder = getHolder();}else{baseHolder = (MyBaseHolder<T>) convertView.getTag();}// 4. 设置内容刷新界面baseHolder.setData(getItem(position));return baseHolder.getRootView();}public abstract MyBaseHolder<T> getHolder();}
写完这两个类后,以后再用到ListView时,只要写一个具体的类继续MyBaseHolder(),这里我写一个Demo示范一下:
假设我的ListView里的item就是一个TextView,那么可以写一个ViewHolder如下:
public class SpecificHolder extends MyBaseHolder<String> {private TextView mTextView;@Overridepublic View initView() {// 1. 加载布局View view = View.inflate(null, R.layout.item_listview, null);// 2. 初始化控件mTextView = (TextView) view.findViewById(R.id.textView1);return view;}@Overridepublic void refreshView(String data) {// 3. 设置内容刷新界面mTextView.setText(data);}}
那么最先贴出来的代码就可以改成如下了:
class MyAdapter extends MyBaseAdapter<String>{public MyAdapter(ArrayList<String> data) {super(data);}@Overridepublic MyBaseHolder<String> getHolder() {return new SpecificHolder();} }
也许你已经乱了,我也是怕以后搞不清了就写个博客来加深记忆,也方便以后回忆。
我们来看下代码执行过程来一下:- 当有一个ListView设置的Adapter是继承我们封装好的MyBaseAdapter之后,然后ListView在内部肯定会走到MyBaseAdapter的getView()方法中,
- 在getView()方法里会调用getHolder()方法,由于这个方法是抽象的,会调用具体子类实现的getHolder()方法;
- 在子类的实现的具体getHolder()方法中,它会new出一个继承了我们的MyBaseHolder类的具体的类,就是上述的SpecificHolder 类。
- 当他new出这个类时,就会在父类MyBaseHolder的构造方法中,调用initView()方法,储存这个ListView的item的布局,并找到这个布局当中的控件,同时给这个布局打了一个Tag。也就是完成了上文提到的getView()方法目的的前3个;
- 然后执行getView()方法中baseHolder.setData(getItem(position));这一句代码。它就会在MyBaseHolder的setData()方法中执行refreshView(data)方法;
- 由于refreshView(data)方法也是抽象的,所以它会走到自己写的SpecificHolder 类的refreshView(data)方法中,由保存的布局控件设置传递过来的数据data,此时完成了第4个目的。
- 最后getView()方法返回储存在holder的item的布局,结束。
在上述整个对ListView的封装当中,对数据的不确定用到了泛型,因为ListView里具体展示的内容是不确定的。
就说到这吧,如果有疑问就私信吧!
更多相关文章
- 实现android图像识别的几种方法
- Android UI开发篇之 ViewPager+九宫格布局 实现左右滑动
- android 测试项目出现 Test run failed: No test results 的解决
- android TextView 的setTextSize方法的使用
- Android Drawer(抽屜)的使用(二)常用的方法:
- 关于Android LinearLayout添加分隔线的方法