今天算是遇到了Android学习上面的一块硬骨头了,目前算是基本理解了,后面还是要时不时的来回顾一下,温故而知新,下面就来回顾一下我今天学习的知识, 今天的知识点就一个那就是关于BaseAdapter,

上一节心得,我专门对数据适配器进行了一个解释,今天在慕课上看到一个比较好的图,这里分享一下。

Android结课总结_数据


从图中我们可以看出为什么需要数据适配器这么一个东西,因为数据源是各种各样的,但是listview它所接受的格式却是固定的,这个时候我们就需要数据适配器这么一个中间件,通过数据适配器来将数据源转换成llistview所能够接受的那种数据。那么BaseAdapter究竟希望它能够干什么能,它主要是比较灵活,可以进行各种的改写,listview有一个缓存机制,如下图:

Android结课总结_缓存机制_02


通过这种机制我们可以大大的减轻内存的开销。

下面我就来具体解释一下关于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中的控件设置数据