今天算是遇到了Android学习上面的一块硬骨头了,目前算是基本理解了,后面还是要时不时的来回顾一下,温故而知新,下面就来回顾一下我今天学习的知识, 今天的知识点就一个那就是关于BaseAdapter,
上一节心得,我专门对数据适配器进行了一个解释,今天在慕课上看到一个比较好的图,这里分享一下。
从图中我们可以看出为什么需要数据适配器这么一个东西,因为数据源是各种各样的,但是listview它所接受的格式却是固定的,这个时候我们就需要数据适配器这么一个中间件,通过数据适配器来将数据源转换成llistview所能够接受的那种数据。那么BaseAdapter究竟希望它能够干什么能,它主要是比较灵活,可以进行各种的改写,listview有一个缓存机制,如下图:
通过这种机制我们可以大大的减轻内存的开销。
下面我就来具体解释一下关于BaseAdapter的事情。首先在这里有一个小技巧,就是利用bean对象,将需要的数据先封装起来。
public class MyBaseAdapter extends BaseAdapter {
// 映射数据
List<ItemBean> datalist;
private long mSumTime;
/*
* LayoutInflater这个类它的作用类似于findViewById()。
* 不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化;
* 而findViewById()是找xml布局文件下的具体widget控件(如Button、TextView等)。
*/
private LayoutInflater mLayoutInflater;
public MyBaseAdapter(Context context, List<ItemBean> datalist) {
this.datalist = datalist;
mLayoutInflater = LayoutInflater.from(context);
}
// 获取数据量
@Override
public int getCount() {
// TODO Auto-generated method stub
return datalist.size();
}
// 获取对应ID的对应Item
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return datalist.get(position);
}
// 获取对应的ID
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TODO Auto-generated method stub
//第一种方式(不加任何的优化)----------------------------------------------
//该函数的作用是为了将xml文件转换成View,这里因为不涉及第二个参数,所以第二个参数为空
View view = mLayoutInflater.inflate(R.layout.item, null);
// 实例化控件
ImageView itemImage = (ImageView) view.findViewById(R.id.iv_image);
TextView itemTitle = (TextView) view.findViewById(R.id.tv_title);
TextView itemContent = (TextView) view.findViewById(R.id.tv_content);
// 去除bean对象
ItemBean bean = datalist.get(position);
// 设置控件的数据
itemImage.setImageResource(bean.itemImageResId);
itemTitle.setText(bean.itemTitle);
itemContent.setText(bean.itemContent);
return view;
第一种方法的一个巨大的缺点就在于,它每次调用getView函数的时候,都要新建一个View对象,这样的话十分占用资源,而且也没有用到ListView的缓存机制,这里我们对其进行一个改进,下面放入getView的代码。
(2)第二种方式 利用 ListView 的缓存机制进行改进
/*
* 第二种方式(加入优化)--第一种方法的弊端是每次调用getview的时候都要新建一个View对象
* 这种方法十分占用内存,因为每次都需要新建一个View对象,这里运用到了listview的缓存机制,也就是
* 该函数的第二个参数convertView,通过使用它可以大大的减少内存的开销 当listview对象非常复杂的时候这种方法十分有效。
*/
if (convertView == null) {
// 这种逻辑的意思就是当convertView为空时,说明是第一次调用,缓存为空,所以将该布局
// 转换为View放入convertView中,当再次调用时不用再像第一种方法一样每次都新建一个view
convertView = mLayoutInflater.inflate(R.layout.item, null);
}
ImageView itemImage = (ImageView) convertView
.findViewById(R.id.iv_image);
TextView itemTitle = (TextView)convertView
.findViewById(R.id.tv_title);
TextView itemContent = (TextView) convertView
.findViewById(R.id.tv_content);
ItemBean bean = datalist.get(position);
itemImage.setImageResource(bean.itemImageResId);
itemTitle.setText(bean.itemTitle);
itemContent.setText(bean.itemContent);
return convertView;
这里和上式相比,利用convertView参数来避免了每次重复新建View对象,但是这种对象还没达到最佳的优化,因为findViewById这个函数每次在执行的时候是需要遍历整个视图树,当视图树比较复杂的时候,这个遍历的时间将会相当的可观。所以这里可以用到Google提出的一个新的方法—-ViewHolder。下面我们来看看代码。
/*
* 第三种方法(最佳版本)这种版本在第二种版本的基础上进一步的优化了findViewById这一个过程
* 因为其实每做一次findViewById的时候,都要遍历整个视图树,如果视图树比较复杂的话
* 遍历的时间将会想当的客观,所以Google专门给了一种数据结构叫ViewHolder
* 同样是运用了缓存的机制,将已经遍历过的主键的ID给存起来
*/
// 获取纳秒时间 更加精确
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = mLayoutInflater.inflate(R.layout.item, null);
holder.image = (ImageView) convertView.findViewById(R.id.iv_image);
holder.title = (TextView) convertView.findViewById(R.id.tv_title);
holder.content = (TextView) convertView
.findViewById(R.id.tv_content);
//将Viewholder与convertView通过setTag绑定起来
convertView.setTag(holder);
}
else {
//getTag()取出关联的ViewHolder
//通过ViewHolder对象找到对应的控件
holder = (ViewHolder) convertView.getTag();
}
ItemBean bean = datalist.get(position);
holder.image.setImageResource(bean.itemImageResId);
holder.title.setText(bean.itemTitle);
holder.content.setText(bean.itemContent);
return convertView;
}
class ViewHolder {
public ImageView image;
public TextView title;
public TextView content;
}
}
最后一种方法不仅利用了ListView的缓存机制,更是通过ViewHolder来实现数据视图的缓存,避免了多次通过findViewById来寻找控件,在今后的程序思路中也应该借用这种思想。
最后对利用ViewHolder来优化BaseAdapter的思路做一下总结。
(1)创建Bean对象,用于封装数据
(2)在构造方法中初始化用于映射的数据list
(3)创建ViewHolder类,创建布局映射关系
(4)判断convertView,为空则创建,并设置tag,如果不为空则通过tag取出对应的ViewHolder
(5)给ViewHolder中的控件设置数据