Android适配器进阶之三(抽象分类适配器)
16lz
2021-01-26
Android多类型抽象适配
我的CSDN博客
我的GitHub
我的GitHub博客
适配器的总结和使用
最新源代码下载
访问密码 9b16
更新:结合单类型抽象适配和分类适配的相关更新,对多类型抽象适配器进行了更新,以下代码是最新的。
上一篇文章介绍了如何进行分类适配,看过的小伙伴可以虽然比较完美的实现了分类适配以及复用,但是代码相当繁琐,我们有两种类型时已经出现了多层if嵌套,如果有三四种类型,估计自己都要转晕了,而且就像我们对单类型适配器抽象时做的,避免重复代码!如果没看过上一篇,建议浏览一下,这里写的很多都是基于第一篇的。
1.抽象ViewHolder
public static class MultiViewHolder extends ViewHolder{ /** * SparseArray */ private SparseArray<View> cacheViews; private View itemView; public MultiViewHolder(View itemView, int viewCount) { super(); this.itemView = itemView; cacheViews = new SparseArray<View>(viewCount); } @Override public View getView(int resId) { View v = cacheViews.get(resId); if (v == null) { v = itemView.findViewById(resId); if (v != null) { cacheViews.put(resId, v); } } return v; }}
是不是发现跟第一篇的ViewHolder一模一样,本身ViewHolder就是维护UI的一个类,这里使用稀疏数组存储UI控件,又包含了获取控件的方法本身已经很完善了,后面我们会对ViewHolder进行进一步的完善附加功能。
2.获取类型的方法getType()
我们在分类适配的时候不可避免的要获得数据的对象,以此作为根据装载不同的布局文件,可能有人会说,在写bean类时加一个获取类型的方法就ok了,那么我们又如何保证在抽象的父类中可以直接调用这个方法呢,强制类实现一个方法!接口
/** 1. 接口,分类适配的对象需要实现的接口,目的是约束实体类实现getType方法 2. 3. @author chendong 4. */public interface MultiEasyAdapterInterface { public int getType();}
没什么好说的,接口很简单
3.抽象分类适配器
适配器的抽象稍微复杂一点
3.1定义类
- 我们需要避免在子类中避免重复编码,同时在子类实现自己的方法,使用抽象父类。
- 我们不了解传递进来的数据是什么类型,使用泛型。
- 我们需要限制传进来的数据必须实现获得获得子类的方法,所以必须要求数据实现MultiEasyAdapterInterface接口,使用泛型限定符确定上限。
public abstract class MultiEasyAdapter<T extends MultiEasyAdapterInterface> extends BaseAdapter {}
3.2成员变量
根据传统的写法,LayoutInflater 和数据集自然是必不可少的
private LayoutInflater layoutInflater;private List<T> datas;
分析一下其他成员,根据上一篇传统分类适配的写法,我们姑且忽略类型的差异
- 每个类型需要一个布局资源文件id
- 每个类型布局文件中字UI控件的个数(用来优化)
- 每个类型一个唯一的键值(用来解决复用Item空指针的问题)
####我们用一个实体类存储这些信息
** * 存储类型信息的实体类 * * @author chendong * @功能:分类适配器配置信息实体类 */public class MultiEasyAdapterEntity { /** * @param type * item类型,int类型变量,Item是什么类型的就填写什么类型 * @param resId * 资源id,对应类型的资源id,你需要装载的资源文件的ID * @param viewCount * 资源文件中对应的需要获取的视图的个数 */ public MultiEasyAdapterEntity(int type, int resId, int viewCount) { super(); this.resId = resId; this.viewCount = viewCount; } private int type; private int resId; private int viewCount; public MultiEasyAdapterEntity() { super(); }}
可能有点繁琐,type和viewCount 可以省略掉,加入type是为了更灵活的获得数据类型,viewCount则是为了优化SparseArray,综上第三个成员变量,就是使用type作为键,MultiEasyAdapterEntity作为值的一个SparseArray,他的作用就是存储不同类型的数据适配时需要的配置信息,有点类似配置文件的意思。
private SparseArray<MultiEasyAdapterEntity> Res4Type;
3.3完善代码
结合分类适配的方法,变量我们已经存储到了SparseArray中,所以代码就很清晰了
/** * version 2<br/> 注意事项:类型数为n时,那么定义的类型必须在0-n-1之间,这是使用listview自带缓存的要求。 * 抽象适配器升级版,可以进行分类适配,使用了模板方法模式,将设置item显示内容的部分抽象到了类外<br/> * @param <T> <br/>必须实现MultiEasyAdapterInterface接口{@link MultiEasyAdapterInterface}<br/> * @author chendong */public abstract class MultiEasyAdapter<T extends MultiEasyAdapterInterface> extends BaseAdapter { private LayoutInflater layoutInflater; private List<T> datas; private SparseArray<MultiEasyAdapterEntity> Res4Type; private Context context; /** * @param context 上下文对象 * @param datas 数据集 * @param Res4Type 资源配置文件{@link MultiEasyAdapterEntity}this is like a config entity */ public MultiEasyAdapter(Context context, List<T> datas, SparseArray<MultiEasyAdapterEntity> Res4Type) { super(); this.layoutInflater = LayoutInflater.from(context); this.datas = datas; this.Res4Type = Res4Type; this.context = context; } protected Context getContext(){ return context; } protected List<T> getDatas(){ return datas; } public void swapData(List<T> datas){ this.datas = datas; notifyDataSetChanged(); } @Override public int getViewTypeCount() { return Res4Type.size(); } @Override public int getItemViewType(int position) { return datas.get(position).getType(); } public int getCount() { return datas.size(); } public Object getItem(int position) { return datas.get(position); } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; /* get the type*/ int type = datas.get(position).getType(); if (convertView == null) { int resId = Res4Type.get(type).getResId(); convertView = layoutInflater.inflate(resId, parent, false); holder = new ViewHolder(convertView, Res4Type.get(type) .getViewCount()); convertView.setTag(holder); bindListener4View(holder,datas.get(position), type, position); } else { holder = (ViewHolder) convertView.getTag(); } bindData4View(holder,datas.get(position), type, position); return convertView; } /** * 绑定数据 * bind data * * @param holder the viewholder * @param type data's type * @param data data */ public abstract void bindData4View(ViewHolder holder, T data, int type,int pos); /** * 绑定监听 * bind listener * * @param holder the viewholder * @param type data's type * @param pos position */ public abstract void bindListener4View(ViewHolder holder, T data, int type,int pos);}
测试
listview = (ListView) findViewById(R.id.listview); list = new ArrayList<Student>(); for (int i = 0; i < 20; i++) { if (i % 5 == 0) list.add(new Student("name" + i, "age" + i, "sex", 3)); else list.add(new Student("name" + i, "age" + i, "sex", i % 2 == 0 ? 1 : 2)); } SparseArray<MultiEasyAdapterEntity> sparseArray = new SparseArray<MultiEasyAdapterEntity>( 3); sparseArray.put(1, new MultiEasyAdapterEntity(1, R.layout.item_type1, 4)); sparseArray.put(2, new MultiEasyAdapterEntity(2, R.layout.item_type2, 4)); sparseArray.put(3, new MultiEasyAdapterEntity(3, R.layout.item_type3, 4)); listview.setAdapter(new MultiEasyAdapter<Student>( getApplicationContext(), list, sparseArray) { @Override public void bindData4View(ViewHolder holder, Student data, int type) { ((TextView) holder.getView(R.id.tv_name)).setText(data .getName()); ((TextView) holder.getView(R.id.tv_sex)).setText(data.getSex()); ((TextView) holder.getView(R.id.tv_age)).setText(data.getAge() + ""); if (type == 3) ((TextView) holder.getView(R.id.tv_type)).setText(data .getType()+""); }@Override public void bindListener4View(ViewHolder holder, Student data, int type) { //绑定监听事件 } });
ps:
1.Student类,四个属性,name,age,sex,type(1,2,3)实现了MultiEasyAdapterInterface接口,这是必须的
2.根据type类型,将配置信息放到稀疏数组中,设置到适配器里
3.在抽象方法中根据type设置不同的类型数据
5.效果
大家可以看到三中布局适配的没有问题,只有蓝色背景的布局显示了type,他是类型3的数据
6.总结
到此适配器进阶就基本结束了,使用单类型抽象适配器,分类抽象适配器可以很简单地进行数据适配操作,我是把它整理成了工具类来使用的,后面一片文章会进行一些总结和优化,并上传源代码,希望有不足的地方大家指正。
更多相关文章
- 【Android】蓝牙开发——经典蓝牙:配对与解除配对 & 实现配对或连
- Qt on Android(安卓)实现App普通全屏、沉浸模式、粘性沉浸模式
- Android(安卓)App调用SDK 登录第一次总是失败的解决方法
- [置顶] android 程序开发的插件化 模块化方法 之二
- [置顶] Android(安卓)轻松实现网络交互模板
- [置顶] Android(安卓)自定义ViewGroup实现整个Item布局竖直跑马
- Android(安卓)bugs——RecyclerView scrollToPosition不会触发sc
- Android坐标系统常用方法属性总结
- Android(安卓)ROM分析(1):刷机原理及方法