关于BaseAdapter的使用及优化心得

1 篇文章 0 订阅
1 篇文章 0 订阅

对于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的实例出来。

参见下面代码

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

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

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

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

参考这个代码

public class PersonAdapter extends BaseAdapter {

    private List<Person> persons;//绑定的数据

    private int resource;//绑定的Layout数据项界面

    private LayoutInflater inflater;//布局填充服务

    public PersonAdapter(Context context, List<Person> persons, int resource) {
         this.persons = persons;
         this.resource = resource;
         inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

      }

     @Override
     public int getCount() {
         return persons.size();
      }

     @Override
     public Object getItem(int position) {
         return persons.get(position);
     }

     @Override
     public long getItemId(int position) {

         return position;

     }

     @Override

     public View getView(int position, View convertView, ViewGroup parent) {

         TextView nameView = null;

         TextView phoneView = null;

         if(convertView == null){

             convertView = inflater.inflate(resource, null);//生成绑定数据的View界面
             nameView = (TextView) convertView.findViewById(R.id.name);
             phoneView = (TextView)convertView.findViewById(R.id.phone);           
             ViewCache cache = new ViewCache();
             cache.nameView = nameView;
             cache.phoneView = phoneView;       
             convertView.setTag(cache);

         }else{

             ViewCache cache = (ViewCache) convertView.getTag();
             nameView = cache.nameView;
             phoneView = cache.phoneView;
         }

         Person person = persons.get(position);

         //下面代码实现数据绑定
         nameView.setText(person.getName());
         phoneView.setText(person.getPhone());

         return convertView;
     }

     /**
     *保存findViewById获得的子控件索引的内部类
     */

     private final class ViewCache{
        public TextView nameView;
        public TextView phoneView;
     }
 }

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

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

  另外也说一下SimplerAdapter使用遇到的一个小问题,当然也是对于新手来说的。就是关于调用notifyDataSetChanged()但是List里面的数据不更改的问题,其实也就是你的数据源更改出错了,例如下面代码它是不会改变数据的。
  


List<HashMap<String,Object>> datas = new ArrayList<HashMap<String,Object>>();

datas = getData();//getData返回新的数据集

adapter.notifyDataSetChanged();//通知数据更新显示,但是List是不会改变数据的显示的

因为在实例化这个Adapter时已经将数据源的引用传进去了,而现在只是修改了数据源data的引用让其重新指向另一个数据集合,所以notifyDataSetChanged()是没反应的,正确的方法应该是这样的。


List<HashMap<String,Object>> datas = new ArrayList<HashMap<String,Object>>();

datas.clear();

datas.addAll(getData());//getData返回新的数据集,返回类型为List<HashMap<String,Object>>

adapter.notifyDataSetChanged();//通知数据更新显示,成功改变

文章转自:http://www.it165.net/pro/html/201407/17852.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值