前言

相信Android开发者对ListView不会陌生,使用ListView需要设置相应的Adapter才能展示数据。Adapter到底是什么东西?让我们来一探究竟。

Adapter

p1.png

通过图1我们可以看出Adapter是View和数据之间的桥梁,并为每一个数据项生成相应的View。Adapter是个接口,定义了子类需要实现的方法,最常见的
方法有:

  • getCount(),总共有多少数据项

  • getItem(),获取对应position 中的item

  • getView(),返回需要展示在屏幕中的View
    一般在自定义Adapter中,只需要实现上述三个方法即可。

Adapter优化前

class ImageAdapter extends BaseAdapter {private Context mContext ;private String[] mList ;public ImageAdapter(Context context, String[] list) {mList = list ;mContext = context ;}@Overridepublic int getCount() {return mList.length;}@Overridepublic Object getItem(int position) {return mList[position];}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {View view = LayoutInflater.from(mContext).inflate(R.layout.gridview_item, parent, false) ;TextView tv = view.findViewById(R.id.text) ;tv.setText(mList[position]);return view;}}

上述大概就是一个最简单的Adapter的实现了吧。通过在getView函数中inflate一个新布局,给相应position设置data,然后将view返回给父控件。咋一看没啥问题,运行也不会有错。但是会有严重的性能问题。想象一下,如果有mList中有100万条数据,我们有必要每次都重新inflate一个新layout,生成一个新view吗?显然是没有必要的,我们的手机屏幕就那么小,可见的View其实也就那几个。那些看不见的View其实是没必要保存的。

View复用原理

p2.jpg

图片来源于此博客。上图清晰展现了View的复用原理。手机屏幕一共能够展示七个item,继续往上滑动,item 1从我们视线消失,此时ListView 会调用Adaper中的getView函数来生成第八个item。此时getView函数中参数convertView就是item 1,我们只需把convertView(item 1)上的数据项全部设置成item 8的数据项即可,这样就不用再重新inflate一个新View出来了。不管是有mList有多大,内存中保存的View的个数永远只是可见的几个。这对程序性能有很大提升。

Adapter优化后

class ImageAdapter extends BaseAdapter {private Context mContext ;private String[] mList ;public ImageAdapter(Context context, String[] list) {mList = list ;mContext = context ;}@Overridepublic int getCount() {return mList.length;}@Overridepublic Object getItem(int position) {return mList[position];}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null ;if (convertView == null) {viewHolder = new ViewHolder();convertView = LayoutInflater.from(mContext).inflate(R.layout.gridview_item, parent, false) ;viewHolder.textview = (TextView) convertView.findViewById(R.id.text);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}// 绑定对应position数据bindViewData(position, viewHolder) ;return convertView;}private void bindViewData(int position, ViewHolder viewHolder) {viewHolder.textview.setText(mList[position]);}private static class ViewHolder {public TextView textview ;}}

以图2为例,每个item都是由View(视图)和Data(数据)组成的。前7个item的convertView都是为空的,因此inflate出了7个新View,当第8个item变成可见时此时convertView 为item 1,再通过bindViewData函数把第八个mList中的数据设置给item 1对应的View,然后直接返回convertView。此时我们看到的item 8其实是由这两个东西组成的:

  • View ------ item 1 的View

  • Data ------ mList 中的第八项

因为item 8和View 和item 1的View结构是一样的(使用同样layout),所以不会造成错误。

View复用导致的问题

问题1:item的状态错乱

p3.png

如图3所示,6跟帖这个View不是所有item都有的,还是拿图2来举例。假设item 1是图3的第一项,item8是图3的第二项。item 1中跟帖这个View是显示的,因为item 8使用的是item 1的View,所以item 8中跟帖的View的状态也是显示的。但是在item 8中跟帖这个View是不应该显示的。这就是复用View导致item状态错乱的问题。解决方法之一就是在bindViewData函数中给layout中的View先全部还原成默认状态即可。

问题2:多线程导致图片加载位置错乱

图片加载往往涉及到网络操作,因此ListView中加载图片时一般都会开启新线程去加载图片。如果不使用View复用方法,直接使用优化前的Adapter,是不会有任何问题的。但如果使用的View复用,就不一定了。

 @Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null ;if (convertView == null) {viewHolder = new ViewHolder();convertView = LayoutInflater.from(mContext).inflate(R.layout.gridview_item, parent, false) ;viewHolder.imageView = (ImageView) convertView.findViewById(R.id.square_image_view);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}String url = mList[position] ;ImageView imageView = viewHolder.imageView ;ImageLoader.loadBitmap(imageView, url);return convertView;}

还是拿图2为例。如上述代码所示,我们使用了复用View方法,并且异步加载图片。由于网络比较慢,item 1到item 7的图片还没加载出来,我们滑动到了item 8(复用了item 1的ImageView),突然网络变好了,item 8 的图片加载完成,我们给Item 8设置了图片。过了一会,item 1的图片也下载完成了,我们又给item 1设置了图片,由于item 1和item 8共用同一个ImageView,因此item 8的图片立马变成了item 1的图片。此时item 8上显示的竟然是item 1对应的图片!解决办法之一就是给ImageView setTag。代码如下:

 ImageView imageView = viewHolder.imageView ;String tag = (String) imageView.getTag();String url = mList[position];if ( !url.equals(tag) ) {//default drawableimageView.setImageDrawable(null);}// set tag to image viewimageView.setTag(url);// load bitmapImageLoader.loadBitmap(imageView, url);

延伸阅读

关于 ScrapView 和 ActiveView

Performance Tips for Android’s ListView
Handling ListView Recycle on Android

转载于:https://www.cnblogs.com/jasonkent27/p/5770844.html

Android-Adapter-View复用机制相关推荐

  1. Android中 RecyclerView的回收复用机制

    作者:金大人的梦 转载地址:https://juejin.cn/post/7094497660983312397 问题归类: 什么是回收?什么是复用? 回收什么?复用什么? 回收到哪里去?从哪里获得复 ...

  2. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  3. android 刷新view位置,Android View刷新机制实例分析

    本文实例讲述了Android View刷新机制.分享给大家供大家参考,具体如下: 一.总体说明 在Android的布局体系中,父View负责刷新.布局显示子View:而当子View需要刷新时,则是通知 ...

  4. Android自定义view之事件传递机制

    Android自定义view之事件传递机制 在上一篇文章<Android自定义view之measure.layout.draw三大流程>中,我们探讨了一下view的显示过程.不太熟悉的同学 ...

  5. Android View事件机制 21问21答

    1.View的坐标参数 主要有哪些?分别有什么注意的要点? 答:Left,Right,top,Bottom 注意这4个值其实就是 view 和 他的父控件的 相对坐标值. 并非是距离屏幕左上角的绝对值 ...

  6. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发机制...

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  7. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  8. android缓冲机制,Android自定义View之双缓冲机制和SurfaceView

    Android自定义View系列 双缓冲机制 问题的由来 CPU访问内存的速度要远远快于访问屏幕的速度.如果需要绘制大量复杂的图像时,每次都一个个从内存中读取图形然后绘制到屏幕就会造成多次地访问屏幕, ...

  9. android adapter 按钮隐藏 第一个无效_Android开发规范

    注释 类/复杂或者不能从方法名字看出意图的方法必须添加注释 /** * @Author: LiuJinYang * @CreateDate: 2020/5/16 9:32 * 网络请求工具类 */pu ...

  10. 一篇文章搞定《RecyclerView缓存复用机制》

    一篇文章搞定<RecyclerView缓存复用机制> 前言 零.为什么要缓存 一.RecyclerView如何构建我们的列表视图 二.缓存过程 三.缓存结构 1.mChangedScrap ...

最新文章

  1. 第十课.蒙特卡洛方法与随机过程
  2. Checkly如何借助Terraform实现零宕机部署
  3. VS 2005 文本编码小技巧
  4. java 变长参数 知乎_变长参数探究
  5. sas sql中有类似mysql的 g_SAS中的SQL
  6. 2021年,朋友圈都在传这8个视频
  7. HTTP与HTTPS协议
  8. 【经验之谈】碰到了放养式的研究生导师,在读研期间该怎么做?
  9. 新生儿肺炎也许不发烧咳嗽
  10. js操作json对象
  11. 安装andriod studio的过程中遇到的问题
  12. 小程序源码:老人疯狂裂变引流视频推广微信小程序-多玩法安装简单
  13. 物联网为什么需要5G?
  14. 前端及后端项目开发工具
  15. python如何拼读英语单词-q开头的英语单词
  16. 用html写一个简单课表
  17. 计算机网络(湖南科技大学)
  18. su: must be suid to work properly错误处理方法
  19. 关于python的多行注释,启动新浏览器,循环语句乘法口诀
  20. Windows下安装图片标注工具LabelImg遇到的问题及解决办法

热门文章

  1. android 开源框架
  2. canvas-画图改进版
  3. [Hyper-V]使用操作系统模板创建新的虚拟机
  4. Spring中原型prototype
  5. 实用!Mybatis中trim标签的使用教程
  6. 今日头条架构演进之路
  7. 微软最强命令行工具发布,强势霸榜GitHub
  8. Linux 平台下 Tomcat 的安装与优化
  9. 运维必读:避免故障、拒绝背锅的六大原则!
  10. 今天给一份 2022 互联网就业指南。