当前位置: 首页 > news >正文

【Android】ListView中getView的原理与解决多轮重复调用的方法

【0】ListView中getView的工作原理:

[1]ListView asks adapter “give me a view” (getView) for each item of the list.(通过getView来获取每个item)

[2]A new View is returned and displayed(获取到后返回显示)

那么如果我们有大量的数据需要显示的时候,每个Item都去重复执行getView中的创建新的View的动作吗?这样做会耗费大量的资源去执行重复的事情,实际上Android为我们提供了一套重复利用的机制叫做Recycler”:

原理简单描述下就是这样:

在一个完整的ListView第一次出现时,每个Item都是Null的,getView的时候会跑到需要inflate一个Item的代码段,假设整个view只能最多显示10个item,那么当滑动到第11个Item的时候,第一个item会放入“recycler”,如果第11个Item和放入“Recycler”的item的view一致,那么就会使用"Recycler"里面的Item来显示,从而不用再重复inflate一次,这样大大节省了创建View的工作,在需要显示大量数据时显得尤为重要。

工作原理的示意图如下:


学习自http://android.amberfog.com/?p=296:

Demo:

这是一个getView的方法,其他细节的Code就不显示了

static class ViewHolder { public ImageView localImageView = null; public TextView localTextView1 = null; public TextView localTextView2 = null; public TextView localTextView3 = null; } public View getView(int paramInt, View paramView, ViewGroup paramViewGroup) { logger.i("This is position:" + paramInt); WrapperSonglist localUserShareSonglistEntity = (WrapperSonglist) getItem(paramInt); if(localUserShareSonglistEntity != null) { ViewHolder holder = null; if(paramView == null) { this.logger.d("convertView == null,Then inflate and findViewById"); paramView = this.mInflater.inflate(R.layout.listitem04, paramViewGroup, false); holder = new ViewHolder(); holder.localImageView = (ImageView) paramView.findViewById(R.id.listitem04ImageView); holder.localTextView1 = (TextView) paramView.findViewById(R.id.listitem04TextView01); holder.localTextView2 = (TextView) paramView.findViewById(R.id.listitem04TextView02); holder.localTextView3 = (TextView) paramView.findViewById(R.id.listitem04TextView03); paramView.setTag(holder); } else { //Used ViewHolder to improve performance this.logger.d("convertView != null,Then findViewById(get Holder)"); holder = (ViewHolder) paramView.getTag(); } if(paramView != null) { this.logger.d("convertView != null,Then SetValue"); String mstr = localUserShareSonglistEntity.getSonglistImage(); int id = localUserShareSonglistEntity.getSonglistId(); holder.localTextView1.setText("[id]:"+id+",bitmap:url:"+mstr); String name = localUserShareSonglistEntity.getSonglistName(); holder.localTextView2.setText("[Name]:"+name); String url = localUserShareSonglistEntity.getSonglistUrl(); holder.localTextView3.setText("[Url]:"+url); } } return paramView; }

当我们第一次启动到Listview的时候如下,只显示了5个Item,那么getView被调用5次:


打印的Log如下:可以看到从0-4都是null,我们需要做inflate的动作,

01-12 17:58:22.144: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:0 01-12 17:58:22.154: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.174: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.174: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:1 01-12 17:58:22.174: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.184: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.184: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:2 01-12 17:58:22.184: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.194: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.194: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:3 01-12 17:58:22.194: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.204: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.204: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:4 01-12 17:58:22.204: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.214: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
当我们小心往下滑动一个位置,刚出现第6个Item,此时第1个还没有消失时,


这个时候只打印了一个获取第6个Item的Log,如下:可以看到第6个Item还是为null,需要inflate出来新的

01-12 18:02:37.623: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:5 01-12 18:02:37.623: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 18:02:37.633: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
如果此时再往下滑动列表,第1个item此时会放入Recycler,第7个Item此时不再是null,而是利用了第1个item的View,如下:


从下面的Log,可以看到从第7个开始就不是null了

01-12 18:52:36.243: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:6

01-12 18:52:36.243: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:89 getView ] - convertView != null,Then findViewById
01-12 18:52:36.243: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
01-12 18:52:36.693: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:7
01-12 18:52:36.693: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:89 getView ] - convertView != null,Then findViewById
01-12 18:52:36.693: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
01-12 18:52:37.024: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:8
01-12 18:52:37.024: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:89 getView ] - convertView != null,Then findViewById
01-12 18:52:37.034: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
01-12 18:52:37.604: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:9
01-12 18:52:37.604: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:89 getView ] - convertView != null,Then findViewById
01-12 18:52:37.604: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue

等所有的item,一共10个都显示之后,不管上下滑动都再也不是NULL了,说明这个时候都是使用Recycle里面的view,而不会再重新inflate了,显然这样节省很多重复的操作

【1】重复多轮调用getView的解决方案

上面的例子我们可以看到,每滑动一次到需要显示的Item的时候就会调用一次getView,理论上是10个Item,均显示一次的话是要调用getView() 10次的,那么为什么有时候很奇怪,10个item显示一次也许会调用getView 20次,甚至40-50次呢?我想肯定很多人都遇到过这个问题

查了很久,其实我也没有找到root cause,只是知道我们在XML文件里面定义listView的时候需要设置height为fill_parent或者是指定的高度值(这个方法明显不靠谱,设置了高度,那么怎么进行不同屏幕的适配)

所以推荐使用下面的,例如:

<ListView android:id="@+id/activity04_list" android:layout_width="fill_parent" android:layout_height="fill_parent"
/>
 

 

网上有人解释说是因为ListView的Item的高度计算方法问题,试了下还是有效果的,希望之后有机会等了解原理后再来解释这个问题,我们可以先这也使用,设置之后会发现没滑动出现一个item才会调用一次getView,这样是合理的调用,而不会出现只有10个item却调用几十次这样比较tricky的事情,也节省了很多资源避免去重复做无用功。

谢谢!



 
 
 
 

                

相关文章:

  • oracle利用触发器实现主键字段自增
  • 函数的重写
  • wx入门(一)
  • ZOJ 1649 Rescue BFS水题
  • Linux 性能分析的前 60 秒
  • C++继承体系下类中属性的能见度总结
  • 案例45-crm练习改写客户列表使用struts2OGNL
  • ZOJ 2913 Bus Pass BFS水题
  • 内置函数——format
  • POJ 1465 Multiple BFS
  • Jenkins之Linux和window配置区别
  • POJ Holedox Moving BFS hash判重
  • mysql5.7的安装
  • MySQL 5.7主从复制与主主复制实现细节分析
  • troubleshooting IMP-00032IMP-00008
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • iOS 颜色设置看我就够了
  • leetcode98. Validate Binary Search Tree
  • Redis字符串类型内部编码剖析
  • Spring Cloud中负载均衡器概览
  • Wamp集成环境 添加PHP的新版本
  • -- 查询加强-- 使用如何where子句进行筛选,% _ like的使用
  • 给第三方使用接口的 URL 签名实现
  • 构建工具 - 收藏集 - 掘金
  • 简单易用的leetcode开发测试工具(npm)
  • 批量截取pdf文件
  • 前端技术周刊 2019-01-14:客户端存储
  • 小程序 setData 学问多
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 云大使推广中的常见热门问题
  • 栈实现走出迷宫(C++)
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • #include<初见C语言之指针(5)>
  • #NOIP 2014# day.2 T2 寻找道路
  • #pragam once 和 #ifndef 预编译头
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • $ git push -u origin master 推送到远程库出错
  • (10)STL算法之搜索(二) 二分查找
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • @Autowired 与@Resource的区别
  • @GetMapping和@RequestMapping的区别
  • @GlobalLock注解作用与原理解析
  • @SpringBootApplication 包含的三个注解及其含义
  • @我的前任是个极品 微博分析
  • [.NET 即时通信SignalR] 认识SignalR (一)
  • [2021 蓝帽杯] One Pointer PHP
  • [ArcPy百科]第三节: Geometry信息中的空间参考解析
  • [C#]winform利用seetaface6实现C#人脸检测活体检测口罩检测年龄预测性别判断眼睛状态检测
  • [C++打怪升级]--学习总目录
  • [Django 0-1] Core.Checks 模块