• Android中AdapterView/Adapter的深度学习


    BaseAdapter的深度学习

      博主工作了几年,也用了几年的ListView等AdapterView控件,但关于Adapter的一些问题并没有深入下去,终于有时间学习总结下关于BaseAdapter的一些较深入的话题。本文涉及三个话题:Adapter的回收机制和效率提升,getItemViewType()/getViewTypeCount()方法以及notifyDatasetChanged()使用的注意点。

    1.Adapter的回收机制和效率提升

      Android在绘制Adapter时,系统首先调用getCount()方法,根据它的返回值得到ListView的长度,然后根据这个长度,调用getView()方法逐行绘制。如果ListView的长度超过了屏幕的长度,android只会绘制显示出来的Item,同时,系统会回收走隐藏的Item。

      如下图所示,此时系统绘制的只有position:4到positon12这9个Item.若按箭头方法滑动,将回收position12,以及绘制position3.

      总的来说,显示出来然后因为拖动而被隐藏的Item才会触发回收。在方法getView(int position, View convertView, ViewGroup parent)中,第二个参数convertView的含义:是代表系统最近回收的View。若整屏能显示9个Item,第一次打开带ListView的控件时,因为并没有回收的View,调用getVIew时,参数convertView的值会为null,否则将不是null,而是最近回收的View的引用.那么合理利用convertView将是提升Adapter效率的关键,否则将会产生大量的new View开销。

     1 @Override
     2     public View getView(int position, View convertView, ViewGroup parent) 
     3     {
     4         Holder1 holder1 = null;
     5         if(null==convertView)    
     6         {
     7             System.out.println("convertView == null" + " position:" + position);
     8             holder1=new Holder1();    
     9             convertView=LayoutInflater.from(mContext).inflate(R.layout.textview, null);
    10             holder1.textView=(TextView)convertView.findViewById(R.id.textview);    
    11             convertView.setTag(holder1);
    12         }    
    13         else    
    14         {
    15             holder1=(Holder1)convertView.getTag();
    16             System.out.println("重用:" + holder1.textView.getText());
    17         }
    18         holder1.textView.setText("position: "+position);   
    19         return convertView;             
    20     }  
    21     
    22     class Holder1    
    23     {
    24         public TextView textView;    
    25     }

    说明一下上图中的例子,按箭头方法拖动,接下来将显示position=4的Item,系统调用getView方法时,第二个参数convertView的值将是position=12的View的引用(最近回收的一个Item的View).[读者可在convertView中用一个TextView记录下每个View的position值,就可发现这个规律]
    精致的逻辑说明:系统绘制Item的View和回收Item的View时有个规则:该Item只要显示出一点点就触发绘制,但必须等该Item完全隐藏之后才触发回收。试验上例时若结果对不上请注意这条说明。

    2.getItemViewType()/getViewTypeCount()方法

      若果Item的View都是同一个模板则用不到这俩方法,但很多情况下我们的AdapterView中可能会用到2个或以上的不同的模板,那这些不同的模板如何复用,那就是这俩方法的作用。

      看下官方对convertView的解释:The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view. Heterogeneous lists can specify their number of view types, so that this View is always of the right type (see getViewTypeCount() and getItemViewType(int)). 意思大概就是: 在有些AdapterView的使用中,比如微博中 有的item中包含图片 有的item包含视频 那么必然的 我们需要用到2种item的布局方式此时如果只是单纯判断“null==convertView”,会造成回收的view不符合你当前需要的布局 而类似转换失败出错退出。

      代码示例:

     1 @Override
     2     public View getView(int position, View convertView, ViewGroup parent) 
     3     {
     4         Holder1 holder1 = null;
     5         Holder2 holder2 = null;
     6         int type = getItemViewType(position);
     7         if(null==convertView)    
     8         {
     9             switch (type) {
    10             case TYPE_1:
    11                 System.out.println("convertView == null" + " position:" + position);
    12                 holder1=new Holder1();    
    13                 convertView=LayoutInflater.from(mContext).inflate(R.layout.textview, null);
    14                 holder1.textView=(TextView)convertView.findViewById(R.id.textview);    
    15                 convertView.setTag(holder1);
    16                 break;
    17             case TYPE_2:
    18                 System.out.println("convertView == null" + " position:" + position);
    19                 holder2=new Holder2();    
    20                 convertView=LayoutInflater.from(mContext).inflate(R.layout.imageview, null);
    21                 holder2.imageView=(ImageView)convertView.findViewById(R.id.imageview);    
    22                 convertView.setTag(holder2);
    23                 break;
    24             default:
    25                 break;
    26             }
    27         }    
    28         else    
    29         {
    30             switch (type) {
    31             case TYPE_1:
    32                 holder1=(Holder1)convertView.getTag();
    33                 System.out.println("重用:" + holder1.textView.getText());
    34                 break;
    35             case TYPE_2:
    36                 holder2=(Holder2)convertView.getTag();
    37                 break;
    38             default:
    39                 break;
    40             }
    41         }
    42         switch (type) 
    43         {
    44         case TYPE_1:
    45             holder1.textView.setText("position: "+position);   
    46             break;
    47         case TYPE_2:
    48             holder2.imageView.setBackgroundColor(colors[position%6]);
    49             break;
    50         default:
    51             break;
    52         }
    53         return convertView;             
    54     }  
    55     
    56     class Holder1    
    57     {
    58         public TextView textView;    
    59     }
    60     
    61     class Holder2
    62     {
    63         public ImageView imageView;
    64     }
    65 
    66     @Override
    67     public int getItemViewType(int position) 
    68     {
    69         if(position < 2)
    70             return TYPE_1;
    71         else if(position%2==0)
    72             return TYPE_1;
    73         else
    74             return TYPE_1;
    75     }
    76 
    77     @Override
    78     public int getViewTypeCount() 
    79     {
    80         return 2;
    81     }
    View Code

      

    这个例子中有两点需要说明:

      1.在getItemTypeView()方法中的返回值不是随便设置的,在SDK中有句话“Note: Integers must be in the range 0 to getViewTypeCount() - 1”。也就是说:返回值得返回必须是0 - (getViewTypeCount()-1)范围内。

      2.关于setTag()和getTag()的理解:初学者对这两个方法可能不能很好的理解,调用setTag("")方法时,可以理解为为View设置了一个标识,然后通过getTag()来获取标识,或者理解为View作为一个容器除了显示一些字符串,图片之外,还可以通过setTag("")方法往其中存放一些数据,然后通过通过getTag()来获取数据。

      说明:我自己在学习这个知识点的过程中,产生了一个奇怪问题:我不用继承父类的这两个方法,自定义方法也可以完成这个功能,想通了之后发现时钻了牛角尖,就不讨论这个问题,若读者也产生了这个问题,可留言交流。

    3.notifyDatasetChanged()使用

      首先看SDK中的说明:Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself. 这句话也好理解。我们在使用该方法过程中,有时候会发现不生效。

    1 //我们通常通过构造器将list赋给自定义的Adapter
    2     ArrayList<String> list = new ArrayList<String>();
    3     MyAdapter adapter = new MyAdapter(context,list);
    4     
    5     list = query(...);
    6     adapter.notifyDataSetChanged();

    这时notifyDataSetChanged()是不会生效的,应该改为:

    1 list.clear();
    2 list.addAll(query(...));
    3 adapter.notifyDataSetChanged();

    这不是android的问题,而是Java特性相关的问题。Java语言的变量中存的是引用。使用"list=query(...);"时,效果是改变了list的引用,而MyAdapter中使用的还是原来的引用,所以notifyDataSetChanged()时不能生效。正确的做法是通过方法来操作对象本身,而不是改变其引用。

    Demo百度云链接:http://pan.baidu.com/s/1dDAAVhZ (找不到更好的链接方式,如果有更好地方式请留言告诉我。)

    Author:Andy Zhai
    2014-01-01  19:20:52

     

     

     

      

      

  • 相关阅读:
    攻城狮在路上(壹) Hibernate(五)--- 映射一对多关联关系
    攻城狮在路上(壹) Hibernate(四)--- 对象标识符(OID)生成机制
    SQL常用方言列表
    maven 依赖查询
    攻城狮在路上(壹) Hibernate(三)--- 属性访问、命名策略、派生属性、指定包名等
    攻城狮在路上(壹) Hibernate(二)--- 第一个hibernate程序
    《精通Hibernate:Java对象持久化技术详解》目录
    攻城狮在路上(壹) Hibernate(一)--- 软件环境、参考书目等一览表
    <转>Hibernate的优、缺点(局限性)
    攻城狮在路上(零)开篇
  • 原文地址:https://www.cnblogs.com/carmanloneliness/p/3500832.html
Copyright © 2020-2023  润新知