`
yehoubin
  • 浏览: 24284 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

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

分享到:
评论

相关推荐

    Google官网上的一个Adapter优化的范例

    总的来说,这个Google的Adapter优化范例旨在帮助开发者理解并实践如何在Android应用中提高列表视图的性能和用户体验。通过学习这个范例,开发者可以掌握ViewHolder模式的使用,理解ListActivity的特性,以及如何编写...

    Android自定义Adapter适配器

    通过查看和学习这个代码,你可以更深入地理解自定义Adapter的工作机制,以及如何根据实际需求进行定制。例如,如果数据源是复杂的对象列表,你需要创建一个ViewHolder类来优化视图的复用,或者添加点击事件监听器来...

    android开发Adapter详解

    ### Android开发Adapter详解 #### 一、Adapter概念与作用 在Android开发中,Adapter是一种用于连接数据源和视图组件...无论是对于初学者还是经验丰富的开发者来说,深入学习Adapter都是提升开发技能的关键步骤之一。

    android之各种Adapter加载数据

    本文将深入探讨Android的Adapter机制,以及如何使用各种Adapter来加载数据。 首先,我们需要理解Adapter的基本概念。Adapter是Android系统中用于连接数据集(如数组、列表等)和可滚动视图(如ListView、GridView、...

    万能Adapter

    本文将深入探讨万能Adapter的核心原理、实现方式以及在Android开发中的应用。 一、Adapter简介 Adapter是Android系统提供的一种机制,主要用于将数据源(如ArrayList)与UI组件(如ListView、GridView)进行绑定。...

    重写Adapter实现Ios TableView效果

    本教程将深入探讨如何通过重写Adapter来达到这一目标,主要关注ListView的使用和适配器的定制。 首先,`ListView`是Android中用于展示可滚动列表的视图组件,它允许开发者在有限的屏幕空间内展示大量数据。与iOS的...

    自定义Adapter适应ListView和GridView

    本文将深入解析如何根据CSDN鸿洋大神的博客,自定义一个通用的Adapter,以满足不同场景下的ListView和GridView的适配器需求。 首先,理解Adapter的作用。Adapter是Android中的一个接口,它是连接数据源(如...

    GridView用自定义Adapter

    本篇将深入探讨如何为GridView创建自定义Adapter,以实现更灵活、个性化的数据展示。 首先,了解Adapter的基本概念。Adapter是Android中一个桥梁类,它连接数据源(如ArrayList)和视图(如ListView、GridView等)...

    RecycleView通用的Adapter适配器

    通过上述内容,我们了解了如何创建一个通用的`RecyclerView.Adapter`,以及如何与`ViewHolder`协作以展示数据。这只是一个基础示例,实际项目中可能需要处理更多复杂情况,比如数据加载、分页、动画等。在使用`...

    canal.adapter-1.1.4.tar.gz

    总的来说,Canal Adapter 1.1.4版本是针对原版问题的修复与优化,旨在提供更稳定、高效和易用的数据同步解决方案。通过这一版本,开发者可以放心地利用Canal Adapter进行大数据环境中的实时数据迁移,提高业务系统的...

    通用的adapter适配器

    6. **优化与扩展**: - 为了进一步优化,可以引入DiffUtil工具类,自动比较数据集的差异,减少不必要的视图更新,提高性能。 - 可以考虑将通用适配器做成库的形式,方便其他项目复用,或者扩展成更复杂的...

    扩展 WebSphere JDBC Adapter 标准入站服务

    本文将深入探讨如何扩展 WebSphere JDBC Adapter,以实现更复杂的标准入站服务,特别是在与 SAP 系统集成时的应用。 首先,理解 WebSphere Adapter 的基本概念至关重要。适配器是 IBM WebSphere Application Server...

    分离holder的低耦合度的adapter

    通过这种方式,Adapter与ViewHolder实现了解耦,使得Adapter可以灵活地处理各种类型的数据,而ViewHolder则专注于UI的展示。同时,由于使用了ButterKnife,减少了手动绑定View的代码,使得ViewHolder更加简洁易懂。...

    这种Adapter的用法

    在IT行业中,Adapter模式是...理解并熟练运用这些Adapter的知识点,可以帮助开发者构建高效、易维护的Android应用。文档“各种Adapter的用法.docx”可能详细阐述了上述各个方面,建议深入学习,以提升Android开发技能。

    Adapter Watch

    "Adapter Watch"是一款专用于网络监控的工具,它能够帮助用户深入洞察计算机的网络状态,以便更好地管理和优化网络性能。该软件提供了丰富的功能,包括查看网卡配置信息、统计TCP和UDP数据、分析IP和ICMP协议的相关...

    ListView中使用自定义Adapter及时更新数据

    ListView通过Adapter来与数据源进行交互,Adapter是连接数据集和视图的桥梁。它负责从数据集中获取数据,并将其转换为ListView可以显示的View。默认的Adapter可能无法满足所有需求,因此我们需要创建自定义Adapter以...

    codelab-adapter-4_9_1-win.zip

    codelab-adapter-4.9.1的版本号表明了这是一个经过多次迭代和优化的软件,4.9.1代表它是在基础版本上进行了至少四次主要更新和九次次要更新后的稳定版本,这通常意味着它拥有较好的性能和兼容性。 在提供的压缩包...

    嵌套LISTVIEW嵌套adapter

    综上所述,"嵌套ListView嵌套adapter"是一种解决复杂数据结构展示的有效手段,尤其适用于说明书或使用手册类应用,它需要开发者对Android的视图组件、适配器机制以及事件处理有深入的理解和实践。在实际开发中,要...

    Laravel开发-illuminate-adapter

    在本文中,我们将深入探讨Laravel开发中的"illuminate-adapter",这是一个基于illuminate库的PSR-6缓存实现。...通过深入理解和使用illuminate-adapter,我们可以更好地利用缓存提升应用程序的性能和响应速度。

Global site tag (gtag.js) - Google Analytics