zoukankan      html  css  js  c++  java
  • Android高手进阶:Adapter深入理解与优化

    一般是针对包含多个元素的View,如ListView,GridView,ExpandableListview,的时候我们是给其设置一个Adapter。Adapter是与View之间提供数据的桥梁,也是提供每个Item的视图桥梁。

    以ListView为例,其工作原理为:

    ● ListView针对List中每个item, adapter都会调用一个getView的方法获得布局视图

    ●我们一般会Inflate一个新的View,填充数据并返回显示

    当然如果我们的Item很多话(比如上万个),都会新建一个View吗?很明显这样内存是接受不了的,Google也不会这么做,Android中有个叫做Recycler的构件,下图是他的工作原理:

    很明显,无论数据中是多少个item,在显示上Recycler只存储其中可见的View在内存中。当向下滑动时,顶部不可见Item直接回移动到下方再次填充数据变为新增项。这样就不用每次都新建一个View了。

    这个也就是我们在Adapter中常见的getView方法的调用,对应此方法我们就能看出,convertView就是每一Item在Recyler之前的布局视图。

    • public View getView(int position, View convertView, ViewGrouppare

    所以,Android已经给我们提供了Recycler机制了,我们就应该利用此机制,而不是每次都去inflate一个View。

    Example

    Don’t

    1. public View getView(int position, View convertView, ViewGroupparent){   
    2.     convertView = LayoutInflater.from(mContext).inflate(R.layout.item_view,null);   
    3.     //dosomething…   
    4.     return converView;   
    5. }   

    Do

    1. public View getView(int position, View convertView, ViewGroupparent){   
    2.      if (convertView ==null) {   
    3.            convertView =LayoutInflater.from(mContext).inflate(R.layout.item_view, null);   
    4.      }   
    5.     //dosomething…   
    6.     return converView;   
    7. }   

    ViewHolder的作用

    之前所说的Recycler模式是为了解决重复inflate时候造成的View资源浪费,还哪有什么方法何可再次优化我们的性能吗?答案是Yes。

    我们还是从getView中的每一个方法调用去查看,发现其实我们拿到convertView的时候,每次都会根据这个布局去findViewById。如下,使我们通常的写法:

    findViewById是在解析layout.xml布局那种其中的子View,解析xml是一个力气活,所以Google也建议我们将这个费力不讨好的活优化起来,所以提出了ViewHolder的概念。

    即,使用一个静态类,保存xml中的各个子View的引用关系,这样就不必要每次都去解析xml了。如下:就是针对上面代码写的一个ViewHolder

    1. if (convertView == null) {                
    2.    convertView = mInflater.inflate(R.layout.item_view, null);             
    3. }    
    4. TextView titleTextView = (TextView) convertView.findViewById(R.id.text));            
    5. ImageView iconImageView = (ImageView)convertView.findViewButId( R.id.icon));    
    6. //DoSomething…   

    findViewById是在解析layout.xml布局那种其中的子View,解析xml是一个力气活,所以Google也建议我们将这个费力不讨好的活优化起来,所以提出了ViewHolder的概念。

    即,使用一个静态类,保存xml中的各个子View的引用关系,这样就不必要每次都去解析xml了。如下:就是针对上面代码写的一个ViewHolder

    1. static class ViewHolder {    
    2.     TextView titleTextView;    
    3.     ImageView iconImageView;    
    4. }    

    但是,在getView方法中我们只能拿到三个参数,position、convertView、viewGroup是拿不到我们自定义的ViewHolder的。所以,我们希望通过convertView拿到ViewHolder只能将其放在tag里。

    下面是一个完整的ViewHolder使用exmaple:

    1. public View getView(int position, View convertView, ViewGroup parent) {   
    2.     ViewHolder holder;   
    3.     if (convertView == null) {   
    4.         convertView = mInflater.inflate(R.layout.item_view, null);   
    5.         holder = new ViewHolder();   
    6.         holder.titleTextView = (TextView) convertView.findViewById(R.id.text);   
    7.         holder.iconImageView = (ImageView) convertView.findViewById(R.id.icon);   
    8.         convertView.setTag(holder);   
    9.     } else {   
    10.         holder = (ViewHolder) convertView.getTag();   
    11.     }   
    12.     holder.titleTextView.setText(DATA[pos].title);   
    13.     holder.iconImageView.setImageBitmap(DATA[pos].bitmap);   
    14.     return convertView;   
    15. }   
    16.    
    17. static class ViewHolder {   
    18.     TextView titleTextView;   
    19.     ImageView iconImageView;   
    20. }   

    Tips. Support.v7中的RecyclerView 就是采用了此思想来制作的。

    多个类型的ViewType

    当我们在Adapter中调用方法getView的时候,如果整个列表中的Item View如果有多种类型布局,如:

    我们继续使用convertView来将数据从新填充貌似不可行了,因为每次返回的convertView类型都不一样,无法重用。

    Android在设计上的时候,也想到了这点。所以,在adapter中预留的两个方法。

    • public int getItemViewType(int position) ; 
    • public int getViewTypeCount();

    只需要重新这两个方法,设置一下ItemViewType的个数和判断方法,Recycler就能有选择性的给出不同的convertView了。 

           Example:

    1. @Override   
    2. public intgetItemViewType(int position) {   
    3.     if (DATA[pos].type == 0) {   
    4.         return 0;   
    5.     } else {   
    6.         return 1;   
    7.     }   
    8. }   
    9.    
    10. @Override   
    11. public int getViewTypeCount() {   
    12.     return 2;   
    13. }   
    14.    
    15. @Override   
    16. public View getView(int position, View convertView, ViewGroup arg2) {   
    17.     TitleViewHolder titleHolder;   
    18.     InfoViewHolder infoHolder;   
    19.     int type = getItemViewType(position);   
    20.    
    21.     if (convertView == null) {   
    22.         switch (type) {   
    23.         case 0:   
    24.             convertView = mInflater.inflate(R.layout.item_view, null);   
    25.             titleHolder = new TitleViewHolder();   
    26.             titleHolder.titleTextView = (TextView) convertView.findViewById(R.id.text);   
    27.             titleHolder.iconImageView = (ImageView) convertView.findViewById(R.id.icon);   
    28.             convertView.setTag(titleHolder);   
    29.             break;   
    30.         case 1:   
    31.             convertView = mInflater.inflate(R.layout.item_view2, null);   
    32.             infoHolder = new InfoViewHolder();   
    33.             infoHolder.titleTextView = (TextView) convertView.findViewById(R.id.text);   
    34.             convertView.setTag(infoHolder);   
    35.             break;   
    36.         }   
    37.     } else {   
    38.         switch (type) {   
    39.         case 0:   
    40.             titleHolder = (TitleViewHolder) convertView.getTag();   
    41.             break;   
    42.         case 1:   
    43.             infoHolder = (InfoViewHolder) convertView.getTag();   
    44.             break;   
    45.         }   
    46.     }   
    47.     switch (type) {   
    48.     case 0:   
    49.         titleHolder.titleTextView.setText(DATA[pos].title);   
    50.         break;   
    51.     case 1:   
    52.         infoHolder.titleTextView.setText(DATA[pos].title);   
    53.         infoHolder.iconImageView.setImageBitmap(DATA[pos].bitmap);   
    54.         break;   
    55.     }   
    56.    
    57.     return convertView;   
    58. }   
    59.    
    60. static class TitleViewHolder {   
    61.     public ImageView iconImageView;   
    62.     public TextView titleTextView;   
    63. }   
    64.    
    65. static class InfoViewHolder {   
    66.     TextView titleTextView;   
    67.     ImageView iconImageView;   
    68. }   

    NotifyDataSetChanged刷新机制

    当ListView中的数据发生了改变,我们希望刷新ListView中的View时,我们一般会调用NotifyDataSetChanged来刷新ListView。看一下它的源码:

    1. public void notifyChanged() {   
    2.     synchronized (mObservers) {   
    3.         // 向每一个子View发送onChanged   
    4.         for (int i = mObservers.size() - 1; i >= 0; i--) {   
    5.             mObservers.get(i).onChanged();   
    6.         }   
    7.     }   
    8. }   

    发 现它针对每一个子View都做了刷新,当然,如果我们的数据都变量还可以理解。但是,一般条件下,我们需要更新的View不多。频繁的调用 NotifyDataSetChanged方法,刷新整个界面不合适。这样会把界面上显示的所有item都全部重绘一次,即使只有一个view的内容发生 了变化。

    所以,我们可以写一个update的方法,来单独刷新一个View

    1. private void updateView(int itemIndex){   
    2.     intvisiblePosition = yourListView.getFirstVisiblePosition();   
    3.     Viewv = yourListView.getChildAt(itemIndex - visiblePosition);   
    4.          ViewHolder viewHolder =(ViewHolder)v.getTag();   
    5.          if(viewHolder!= null){   
    6.                viewHolder.titleTextView.setText("我更新了");   
    7.          }      
    8. }   

    Adapter中的网络图片优化

    ListView中的每一项Item基本都会带着网络图片,当item比较多的时候,过多的网络请求和过多的图片存储都会是ListView变慢变卡。

    所以针对其做一下优化:

      ●  采用线程池进行网络图片请求,网络图片请求获取后使用本地缓存处理(LRUCache),内存+本地文件缓存。当然,为了防止内存溢出与回收不及时,需要使用弱引用(WeakReference)来存储内存中的图片。

      ●  对网络中取到的图片进行按比例缩放,以减少内存消耗。

      ●  滑动的时候不需要对网络图片进行请求。因为,网络请求一般比较耗时,某Item的图片,在请求来的时候如果被Recycler换掉,图片就会对应不上该Item。 

    Tips.网络请求的工具类比较多不方便举例子,但是使用比较频繁的网络图片请求工具类就是Volley了,Volley提供了一个ImageLoader的工具类和NetworkImageView的网络图片请求View

    本文链接:http://www.eoeandroid.com/thread-536377-1-1.html

    【编辑推荐】

    【责任编辑:chenqingxiang TEL:(010)68476606】

     

  • 相关阅读:
    cf B. Sereja and Suffixes
    cf E. Dima and Magic Guitar
    cf D. Dima and Trap Graph
    cf C. Dima and Salad
    最短路径问题(floyd)
    Drainage Ditches(网络流(EK算法))
    图结构练习—BFSDFS—判断可达性(BFS)
    Sorting It All Out(拓扑排序)
    Power Network(最大流(EK算法))
    Labeling Balls(拓扑)
  • 原文地址:https://www.cnblogs.com/yezhennan/p/5113502.html
Copyright © 2011-2022 走看看