**原创不易,感谢阅读!笔者能力有限,欢迎在评论区交流指正,不胜感激٩(๑>◡<๑)۶ **
Android自学笔记(一):Adapter设计思想浅析
为什么要自定义一个BaseAdapter
Adapter的设计思路,就像是C++中的迭代器,我们都知道在C++当中,迭代器是容器和算法之间进行遍历和处理操作的桥梁。对于Android的交互模式来说,Adapter就是这个桥梁作用了。
所以自定义BaseAdapter,就相当于我们需要自己设计一个迭代器,根本原因,我认为是为了适配自定义的前端设计、自定义事件逻辑,以及适配自定义的结构体所需。
自定义BaseAdapter的使用场景
自定义的BaseAdapter用来作什么呢?用来满足这样的交互场景:
获得数据以用来进行适配(相当于取容器数据。对于很熟悉Android开发的
- 程序员大神们,你们一定很熟悉了,例如一些存放字符串、图片之类的数组,或者是直接从SQLite去获取,这个我还不太熟悉)
- 获得数据之后用来适配什么呢?是用来适配前端的UI布局(例如一个ListView,我们一般需要为其设计整体的layout布局,然后我们需要对ListView中的每一个Item也设计其布局。)
如何设计一个自定义的BaseAdapter
设计BaseAdapter接受怎样的数据类型
也就是说:为BaseAdapter这个类添加相应的成员变量。
BaseAdapter的数据迭代策略
需要重载一些重要的方法:
// 参考链接:https://www.runoob.com/w3cnote/android-tutorial-listview.html
/**
/* 获取Adapter所拿到的容器的大小
**/
@Override
public int getCount() {
return mData.size();
}
/**
/* 取Adapter当前迭代位置的数据元素
**/
@Override
public Object getItem(int position) {
return null;
}
/**
/* 获取当前迭代所处的位置
**/
@Override
public long getItemId(int position) {
return position;
}
这里的position我觉得很有意思,可以理解为这个适配器内部使用的迭代指针(比如Adapter::Iterator?本人对C++编程风格比较心水哈哈哈:-D)。
// 参考链接:https://www.runoob.com/w3cnote/android-tutorial-listview.html
img_icon.setBackgroundResource(mData.get(position).getaIcon());
txt_aName.setText(mData.get(position).getaName());
txt_aSpeak.setText(mData.get(position).getaSpeak());
为BaseAdapter的元素(Item)数据适配对应的布局(Item-layout)
ListView 中的每一个 Item都有其固定的布局。
// 参考链接:https://www.runoob.com/w3cnote/android-tutorial-listview.html
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);
在ListView-Item-layout下,指定各个数据元素应当和该布局中的哪些元素相对应:
// 参考链接:https://www.runoob.com/w3cnote/android-tutorial-listview.html
ImageView img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
TextView txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);
TextView txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);
这些R.id都是在Item-layout中设定的。
此外,其中可能涉及到读取Resource的图片资源,不是字符串或者数字这样的简单数据。例如icon image。
// 参考链接:https://www.runoob.com/w3cnote/android-tutorial-listview.html
img_icon.setBackgroundResource(mData.get(position).getaIcon());
至于数据是自己写死的,还是通过SQLite获取的,还是其他方式,和这里没有关系。对于Adapter而言只要拿到对应的容器数据就好了。
在Activity中使用自定义的BaseAdapter
首先实例化一个BaseAdapter对象。以下的代码中,完成了两件事情:
-
实例化了一个BaseAdapter对象。
(这样说不太严谨,应该说AnimalAdapter是我们基于BaseAdapter这个类自定义了一个AnimalAdapter类,然后用AnimalAdapter类实例化了一个AnimalAdapter对象。但是本文讨论的是BaseAdapter,所以我觉得说实例化BaseAdapter对象这种表述更加符合本文的初衷)
-
利用构造函数,同时完成了BaseAdapter对象的数据适配。
// 参考链接:https://www.runoob.com/w3cnote/android-tutorial-listview.html
mAdapter = new AnimalAdapter((LinkedList<Animal>) mData, mContext);
然后我们为我们已经好的ListView布局添加上这个BaseAdapter,让我们的适配器与布局相关联。
首先将ListView对象和ListView-layout关连起来:
// 参考链接:https://www.runoob.com/w3cnote/android-tutorial-listview.html
list_animal = (ListView) findViewById(R.id.list_animal);
然后为ListView对象指定BaseAdapter:
// 参考链接:https://www.runoob.com/w3cnote/android-tutorial-listview.html
list_animal.setAdapter(mAdapter);
然后……然后应该就完成了叭?✧(≖ ◡ ≖✿
其实并不是。因为这里的Adapter只有数据呈现,我们只是把拿到的容器数据读出来,然后在前端做了一个数据呈现而已。顶多是ListView可以如同滚动条一样的滑来滑去。
其实,我们经常会要在ListView中勾选一些按钮,或者是RadioButton之类的,总之交互的形式可以有很多种,这意味着Adapter也需要对相应的点击事件有所处理。举个形象一些的例子,例如手机短信,我希望点击某条短信之后,该短信能够显示“已读”,然后左下角的蓝色气泡会消除掉,然后标题字体的颜色会变成灰色,之类的效果。
为ListView中的Item添加点击事件逻辑
这一块我还在学习当中,后续会完善。(未完待续)
优化BaseAdapter的代码
这一节里让我们优化一下BaseAdapter的代码。优化点包括两个部分,convertView和ViewHolder,它们的作用是使得不用每一次都加载layout布局,减少重复的运行量。
此处优化是通过单例模式实现的,使用了静态类。代码如下:
// 参考链接:https://www.runoob.com/w3cnote/android-tutorial-baseadapter.html
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null){
// 为convertView加载Item-layout布局
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);
holder = new ViewHolder();
// 为视图持有者关联convertView中所保存的对应布局元素id
holder.img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
holder.txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);
holder.txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);
// 将视图持有者存储到convertView中
convertView.setTag(holder);
}else{
// convertView已经设置过了,那么此时就复用convertView中的视图持有者
holder = (ViewHolder) convertView.getTag();
}
// 这里和之前没有优化过的BaseAdapter代码是统一的
holder.img_icon.setBackgroundResource(mData.get(position).getaIcon());
holder.txt_aName.setText(mData.get(position).getaName());
holder.txt_aSpeak.setText(mData.get(position).getaSpeak());
return convertView;
}
// 在这个例子中,视图持有者持有三种成员数据,这和我们输入的容器元素数据(结构体)是统一的。
static class ViewHolder{
ImageView img_icon;
TextView txt_aName;
TextView txt_aSpeak;
}
虽然这样子的写法相比没有优化过的版本而言,要拗口不少,但是这其实是更加通用的形式,也是大部分人使用的开发模板。通过一点点慢慢梳理,我们还是能由此理明白这个代码为什么要写成这个样子了。