Android ListView封装(代码优化):抽取方法共性,封装 BaseAdapter 和 ViewHolder
来源:互联网 发布:linux 调用中文输入法 编辑:程序博客网 时间:2024/05/18 01:07
大多App都会使用到的基本控件 ——- Listiew,特别像新闻浏览类的比如说“今日关注”,或者“应用宝”这种汇集手机软件集合的。而且大家都知道 需要给每个单独的 ListView 搭配相应的适配器 Adapter 。如果你的项目中使用ListView 的频率很少甚至没有,那我不建议你对 ListView 进行抽取封装,但是!如果它的使用渗透到App中大多页面时,你必须考虑 对Adapter的公共方法进行抽取封装到单独的类中,来避免整个项目代码的冗杂,使代码结构规范化!
一. 未封装版 ListView
HomeFragment.java@Overridepublic View onCreateSuccessView() { ListView view = new ListView(UIUtils.getContext()); initData(); view.setAdapter(new HomeAdapter()); return view;}private void initData() { for (int i = 0; i < 50; i++) { mList.add("测试数据" + i); }}class HomeAdapter extends BaseAdapter { @Override public int getCount() { return mList.size(); } @Override public String getItem(int position) { return mList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = View.inflate(UIUtils.getContext(), R.layout.list_item_home, null); holder = new ViewHolder(); holder.tvContent = (TextView) convertView .findViewById(R.id.tv_content); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.tvContent.setText(getItem(position)); return convertView; }}static class ViewHolder { public TextView tvContent;}
这里我们主要 把重点放在 Adapter 和 ViewHolder 的抽取封装,首先简单分析其逻辑组成:
1.首先看到我自定义的 HomeAdapter 继承的是 BaseAdapter 。继承的四个方法中,前三个:getCount 、getItem 、getItemId 看的出来方法及其简单,只涉及到一个不确定量——显示的数据类,易封装,使用泛型定义数据类型即可。
但是getView方法中步骤略复杂,首先梳理清楚方法里的逻辑,才好进一步的封装:
(1)加载布局文件,布局转换(xml —> view)
(2)初始化控件(finViewById)
(3)给ViewHolder设置标记(setTag),便于下次复用
(4)给控件设置数据
static class ViewHolder { public TextView tvContent;}
2. ViewHolder 类中定义的是一个单独条目中的所有控件声明,也许你会发现 不同的ListView中适配器 Adapter中有共性的方法,但是 不同页面中展示条目不尽相同,根本无法提取。事实的确是这样,但是Adapter中肯定会用到 ViewHolder类 ,而且getView方法中的部分逻辑还要依靠这个类去完成,所以我们还是 要继续提取一个基类 BaseHolder,至于取决于不同页面逻辑也不同的地方,封装一个抽象方法丢给子类去实现即可!
二. BaseAdapter封装
1.抽取BaseAdapter的共性方法,封装到MyBaseAdapter类
/** * 数据适配器的基类 * 展示的数据类型不确定,故使用泛型! */public class MyBaseAdapter<T> extends BaseAdapter { private ArrayList<T> list; public MyBaseAdapter(ArrayList<T> list) { this.list = list; } @Override public int getCount() { return list.size(); } @Override public T 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) { return null; }}
首先我们对 继承Basedapter的三个方法:getCount 、getItem 、getItemId 进行封装,由于方法中涉及到 展示的条目数据,这里要增加一个 构造方法 —— 用来接收 数据集合。至于展示的数据集合类型无法确定,所以才用泛型来定义数据集。定义的类也要加上泛型。MyBaseAdapter<T>
2. getView 封装
这里只剩下getView方法还未封装,我们再次复习其中逻辑:
(1)加载布局文件,布局转换(xml —> view)
(2)初始化控件(finViewById)
(3)给ViewHolder设置标记(setTag),便于下次复用
(4)给控件设置数据
MyBaseAdapter.java@Overridepublic View getView(int position, View convertView, ViewGroup parent) { BaseHolder<T> holder = null; if (convertView == null) { // 在初始化holder的同时,已经对布局进行了加载,也给view设置了tag holder = getHolder(position); } else { holder = (BaseHolder<T>) convertView.getTag(); } // 刷新界面,更新数据 holder.setData(getItem(position)); return holder.getRootView();}// 返回BaseHolder的子类,必须实现public abstract BaseHolder<T> getHolder(int position);
首先初始化我们从ViewHolder中提取的基类泛型 BaseHolder<T>
。
(1)前3点逻辑 主要依赖于具体的页面,不同的页面应当对应不同的ViewHolder,所以在这个封装一个抽象方法getHolder ,交由 子类继承 MyBaseAdapter去实现!
(2)第四点逻辑 ,更新数据,直接调用 BaseHolder中的方法。
三. BaseHolder封装
1.抽取ViewHolder的功能,封装到BaseHolder类
由以上可知,BaseHolder类中应做的事
1. 初始化item布局,2. findViewById方法:一个抽象方法,由子类在初始化布局时实现!
3. 给view设置tag:在BaseHolder的构造方法中实现!
4. 刷新界面:抽象方法,由子类去实现!
所以,BaseHolder相当于是对getView方法的封装!
public abstract class BaseHolder<T> { private View mRootView;// item的布局对象 private T data;// item对应的数据 public BaseHolder() { mRootView = initView();// 初始化布局 mRootView.setTag(this);// 给view设置tag } // 初始化布局的方法必须由子类实现 public abstract View initView(); // 返回布局对象 public View getRootView() { return mRootView; }; // 设置数据 public void setData(T data) { this.data = data; refreshView(data); } // 获取数据 public T getData() { return data; } // 刷新界面,更新数据,子类必须实现 public abstract void refreshView(T data);}
——–以上已经封装完毕!———–
四. BaseHolder耦合于getView
解析BaseHolder:
若按照以上解释,那BaseHolder只需要两个抽象方法和构造函数即可,可并非如此!既然BaseHolder是对getView方法的一个封装,那它需要完成getView中的所有功能,可它跟getView方法是耦合的,所以需要相互配合,再查看封装好的 getView方法:
MyBaseAdapter.java@Overridepublic View getView(int position, View convertView, ViewGroup parent) { BaseHolder<T> holder = null; if (convertView == null) { // 在初始化holder的同时,已经对布局进行了加载,也给view设置了tag holder = getHolder(position); } else { holder = (BaseHolder<T>) convertView.getTag(); } // 刷新界面,更新数据 holder.setData(getItem(position)); return holder.getRootView();}// 返回BaseHolder的子类,必须实现public abstract BaseHolder<T> getHolder(int position);
1. 这里的holder调用的是 由子类实现的抽象方法,
// 在初始化holder的同时,已经对布局进行了加载,也给view设置了tag holder = getHolder(position);
具体就是new 一个 继承BaseHolder 的子类,所以我们new完这个子类后,需要完成getView的1.2.3点逻辑。所以在 Baseholder中的构造方法中完成此3点逻辑(抽象方法)。
// 刷新界面,更新数据 holder.setData(getItem(position));
2. 而这里的设置数据的方法不适合直接设为抽象,因为涉及到数据的传递(数据类型不确定,依旧为泛型)。方法中第一步是接收数据,第二步才是刷新数据。我们的做法是把第二步的逻辑(刷新数据)单独拿出来,封装成一个抽象方法,留给子类BaseHolder去实现!
return holder.getRootView();
3.getView 的最后一步 —— 将当前item的View返回!可是我们具体View的初始化都交由子类实现了,BaseHodler 并无View。这时我们就要在BaseHodler 单独定义一个方法getRootView()来返回当前item的布局。
以上,才是BaseAdapter 和ViewHolder的完美搭配,即MyBaseAdapter 和 BaseHodler 完美搭配
举一个栗子:
MyBaseAdapter 的继承:
HomeFragment.javapublic class HomeFragment extends BaseFragment { private ArrayList<String> data; @Override public View onCreateSuccessView() { initData(); ListView view = new ListView(UIUtils.getContext()); view.setAdapter(new HomeAdapter(data)); return view; } private void initData(){ for (int i = 0; i < 50; i++) { data.add("测试数据" + i); }class HomeAdapter extends MyBaseAdapter<AppInfo> { private ArrayList<String> data; public HomeAdapter(ArrayList<AppInfo> data) { super(data); } @Override public BaseHolder<AppInfo> getHolder() { return new HomeHolder(); }}
BaseHodler 的继承:
public class HomeHolder extends BaseHolder<AppInfo> { private TextView tvContent; @Override public View initView() { // 1. 加载布局 View view = UIUtils.inflate(R.layout.list_item_home); // 2. 初始化控件 tvContent = (TextView) view.findViewById(R.id.tv_content); return view; } @Override public void refreshView(Stringdata) { tvContent.setText(data); }}
喔~~~~大家感觉到了吗?!代码量骤减啊!!!好处自是不必多说,用心体会~
希望对你有帮助 :)
- Android ListView封装(代码优化):抽取方法共性,封装 BaseAdapter 和 ViewHolder
- Android ListView框架搭建:抽取Adapter共性的方法与属性进行封装
- ListView 优化之ViewHolder封装
- 对BaseAdapter和ViewHolder的封装
- listview的优化---viewHolder的封装
- 利用ViewHolder优化BaseAdapter(ListView)
- Android 对ListView和RecyclerView的两个BaseAdapter封装分享
- Android BaseAdapter和ViewHolder 优化 解决ListView的item抢焦点问题和item错乱问题
- 116.黑名单设置的优化(封装BaseAdapter的方法)
- Android MVP+Adapter+ViewHolder抽取-ListView最终优化
- BaseAdapter封装优化
- android ViewHolder 封装写法
- ListView之ViewHolder类封装
- [android开发]封装BaseAdapter, 简便代码
- 代码优化——抽象ViewHolder和BaseAdapter
- Android ListView优化 如何省略ViewHolder方法
- Android万能适配器(一)封装ViewHolder
- Android - ViewHolder优化ListView
- Codeforces Round #239 (Div. 2) AB (水)
- 1.dubbo学习之背景与架构
- js input框 输入数字校验
- [LeetCode] 137. Single Number II
- Androidstudio项目分享到Git@OSC托管的两种方式
- Android ListView封装(代码优化):抽取方法共性,封装 BaseAdapter 和 ViewHolder
- 初涉Rx套餐 之RxBinding(让你的事件流程更清晰)
- ubantu下最强最全Git入门
- 按成绩蛇形分班算法
- 5.单件模式(Singleton Pattern)
- 剑指Offer:二叉搜索树的后序遍历序列
- Codeforces Round #239 (Div. 2) C Triangle(暴力)
- java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver问题解决
- LeetCode - Longest Substring Without Repeating Characters