zoukankan      html  css  js  c++  java
  • 关于BaseAdapter的使用及优化心得(一)

      对于Android程序员来说,BaseAdapter肯定不会陌生,灵活而优雅是BaseAdapter最大的特点。开发者可以通过构造BaseAdapter并搭载到ListView或者GridView这类多控件布局上面,实现软件所需要的布局效果。同时,BaseAdapter也是适配器里面最基础的一个类,其他的例如SimpleAdapter、ArrayAdapter都是直接或者间接继承BaseAdapter,所以说学好BaseAdapter基本就熟练掌握了适配器的使用了。

      本文需要具备一些Android基础知识的前提下才能更好的理解,因为本文并不是介绍BaseAdapter的基本使用方法,如果对于BaseAdapter不了解可以去参考一下文档。在这里主要是介绍一下BaseAdapter的实现细节和怎么优化BaseAdapter的设计,从而节省我们宝贵的手机运行资源。对于老鸟高手来说,这可能是一个常识了,还是主要给那些像我以前一样刚刚入门的开发者的一点点参考,也希望高手来多多指正。

     话不多说,我们看一看某些社交软件的界面(网上找的。。。。。)

      这个列表项就是用ListView作为布局,搭载SimpleAdapter或者BaseAdapter作为数据源做成的界面效果。一般来说,很多软件的界面都运用这个方法来实现想要的布局效果。我在这里说明一下后面说到的名词,首先这是一个列表项,里面有各个数据项,例如图片+文件传输助手+test+时间就是一个数据项,它是一个View视图,这张图片里面一共就有8个数据项,由8个View视图按垂直方向排列而成。OK,我们开始步入正题。

      假设,我们还是用例子讲解比较好理解,所以呢我们假设这个列表里面一共有40个数据项,而作为手机界面的限制,它一次只能显示8个数据项,同时它的另外32个数据项是隐藏状态。而显示这8个数据项就需要8个View实例,也就是需要实例化8个View的子类来显示内容,此时系统就有8个View实例占据内存空间。那如果我们把手指往下一滑动呢,自然顶端的数据项就会被隐藏,而底端的数据项自然就显示出来,那么这时候需要在new一个新的View实例来吗?如果还是继续创建实例,那么系统内存空间就有9个View实例了,可是我们还是只需要8个View实例就可以在手机屏幕完全显示,而顶端的被隐藏的实例话是可以不存在的了。所以这么来说,这样就浪费了手机的运行资源了。

      为了解决这个问题,我们在BaseAdapter中可以对View进行重用,也就是将被隐藏的View改变为将要显示的View并返回出来。

      在BaseAdapter中,这个抽象方法非常重要,它就是根据position的值返回对象的数据项的View视图,所以我们主要也是围绕这个方法来达到效率上优化的目的。

       public View getView(int position, View convertView, ViewGroup parent) { return null; } 

      简单介绍一下这个函数的参数,position是ID索引值,也就是ListView的对应的数据项的序号,而convertView则是View视图复用的基础,如果为第一个页面的数据,它的值为null,所以我们可以根据convertView的值来决定我们是否要new一个View的实例出来

    参见下面代码

     1 @Override
     2 public View getView(int position, View convertView, ViewGroup parent) {
     3   if(convertView == null){
     4     convertView = new TextView(context);//新建一个字符串控件
     5     ((TextView)convertView).setText(position);//实现其是第几个数据项
     6   }
     7   else{
     8     ((TextView)convertView).setText(position);//重新修改其是第几个数据项
     9   }
    10   return convertView;
    11 }

      为了简单起来,我这里的View只是一个简简单单的字符串控件,在代码中我们可以看到,先对convertView进行判断,如果其值为null,则说明该方法此时返回的View是第一页的视图,所以需要new出一个View的实例,然后对其进行设置显示的内容并返回(这里设置的内容为position)。如果其值不为null,则说明此时的View视图是可以复用其他已经隐藏的View实例,而convertView的索引就是这个隐藏的实例,自然我们不用重新创建一个新的View实例而直接对convertView进行重新设置数值并返回便可。

      这样就保证了程序里面只有足够界面显示的View实例的个数存在,而不会有多余的View来占用系统资源,由此也提高了一点点效率。

      这是BaseAdapter的效率优化的一个方面,接下来我们还要说一下另外一个方面,说明白了也就是一个小技巧来提高效率。对于一个BaseAdapter来说,显示的界面肯定不是仅仅的一两个字符串控件(不然我们就用ArrayAdapter了多简便),而一般是加载一个根据layout生成的布局控件,相对来说这个的布局控件会比较复杂,我们会对其的子控件进行获取并赋值返回,所以我们经常要调用findViewById()这个方法来或者子控件的引用。而findViewById()会比较消耗系统资源,频繁的调用在我们快速滑动界面时会加重手机的负担,所以我们也可以对这个地方进行一些小小的改进。

    参考这个代码

     1 public class PersonAdapter extends BaseAdapter {
     2     private List<Person> persons;//绑定的数据
     3     private int resource;//绑定的Layout数据项界面
     4     private LayoutInflater inflater;//布局填充服务
     5     
     6     public PersonAdapter(Context context, List<Person> persons, int resource) {
     7         this.persons = persons;
     8         this.resource = resource;
     9         inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    10     }
    11     @Override
    12     public int getCount() {
    13         return persons.size();
    14     }
    15     @Override
    16     public Object getItem(int position) {
    17         return persons.get(position);
    18     }
    19     @Override
    20     public long getItemId(int position) {
    21         return position;
    22     }
    23     @Override
    24     public View getView(int position, View convertView, ViewGroup parent) {
    25         TextView nameView = null;
    26         TextView phoneView = null;
    27         if(convertView == null){
    28             convertView = inflater.inflate(resource, null);//生成绑定数据的View界面
    29             nameView = (TextView) convertView.findViewById(R.id.name);
    30             phoneView = (TextView) convertView.findViewById(R.id.phone);
    31             
    32             ViewCache cache = new ViewCache();
    33             cache.nameView = nameView;
    34             cache.phoneView = phoneView;        
    35             convertView.setTag(cache);
    36         }else{
    37             ViewCache cache = (ViewCache) convertView.getTag();
    38             nameView = cache.nameView;
    39             phoneView = cache.phoneView;
    40         }
    41         Person person = persons.get(position);
    42         //下面代码实现数据绑定
    43         nameView.setText(person.getName());
    44         phoneView.setText(person.getPhone());
    45         
    46         return convertView;
    47     }
    48     /**
    49     *保存findViewById获得的子控件索引的内部类
    50     */
    51     private final class ViewCache{
    52         public TextView nameView;
    53         public TextView phoneView;
    54     }
    55 }

      重点还是放在getView()这个方法上。在代码里面,我写了一个ViewCache的私有内部类,主要用来保存要来复用的View所需要获得的子控件的索引值。稍微解释一下代码,如果convertView为null,则说明需要new出一个View实例,在这里就是调用Inflater这个系统服务根据resource这个界面的ID值来生成一个View实例,然后用findViewById这个方法来或者其中的子控件的值,并保存在内部类ViewCache中,放入Tag中,setTag(object)是可以放入一个任意的类来保存成为缓存,因为其是Object。最后设置View子控件的内容并返回。如果不为null呢,现在则需要重用View实例了,所以我们也顺带重用一下保存在Tag里面的ViewCache的类成员变量的子控件索引值,而不需要重新去一个一个调用findViewById()这个方法,在界面足够复杂时这样是可以节省很多系统资源的。

      这就是BaseAdapter提高效率的两种小技巧,如果大牛有其他更好的技巧,也希望能分享分享用来学习一下。

    作者:enjoy风铃
    出处:http://www.cnblogs.com/net168/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则下次不给你转载了。

  • 相关阅读:
    udelay、mdelay、ndelay、msleep使用比较说明
    linux多线程驱动中调用udelay()对整个系统造成的影响(by liukun321咕唧咕唧)
    linux设备驱动那点事儿之平台设备理论篇
    misc_register、 register_chrdev 的区别总结
    platform_driver与file_operations两种方法开发led驱动
    rc522 ,pn544区别
    内核驱动中常见的miscdevice、platform_device、platform_driver
    file_operations结构2
    file_operations结构体解析 1
    android5.0问题
  • 原文地址:https://www.cnblogs.com/net168/p/3858257.html
Copyright © 2011-2022 走看看