ListView的常用优化

来源:互联网 发布:软件外包保密协议 编辑:程序博客网 时间:2024/06/05 02:38

#ListView的常用优化

ListView概述

  • Android应用开发中,ListView是最在 Android 应用开发中,ListView是最为常见的组件之一。它将数据以列表的形式展现出来,在我们平时的开发中也是很常见。一般而言,一个ListView 由以下三个元素组成:
    1. View: 用来展示列表的 view,通常是一个 xml 文件所指定的。
    2. 适配器:用来把数据映射到 ListView 上,可以理解为 ListView 界面和数据之间的纽带和桥梁。
    3. 数据:具体被映射的数据和资源,可以是字符串、图片等。

ListViewde的工作原理

  • 接下来我们简单介绍一下 ListView加载数据的原理。有了这方面的了解后再说优化才行。ListView 针对每个 item(列表项)都要求 Adapter(适配器)“返回一个视图”(getView)。
  • ListView 在开始绘制的时候系统会首先调用 getCount()函数,根据它的返回值得到 ListView 的长度,然后根据这个长度,逐次调用 getView()方法一行一行的绘制ListView 的每一项。getCount()方法的返回值是几就显示几行。
  • 具体怎么绘制呢?当我们调用 getView()方法时首先会通过加载布局文件生成一个新的 View 对象(实际上是一个 ViewGroup),然后通过该对象将布局文件中的各个组件实例化(也就是 findViewByid()),这样便可将数据对应到各个组件上了。但是加载布局文件属于I/O 操作,是很耗时的,当我们的数据量非常庞大或者列表项非常多的时候,很容易导致Anr(应用程序无响应)现象。

    那我们到底该如何解决这个问题呢?

ListView的真正优化处理方案

  • .复用 ConvertView;使用 ViewHoldersetTag
  • ListView 中数据的分批及分页加载
  • .异步加载图片及图片的缓存处理

一. 1. 重点内容复用convertView

  • 所谓的复用,就是循环反复的利用。假如说我们有一百条列表项,我们没有必要一一为他们创建新的对象,我们只要创建三个或者五个item 对象,之后对他们循环利用即可。
  • 其实 Android 系统本身为我们考虑了 ListView 的优化问题。在 getView()方法中系统就为我们提供了一个复用 View 的历史缓存对象 convertView,当显示第一屏的时候,每一个item都会新创建一个 View 对象,这些 View 都是可以被复用的。
  • 此时 convertViewgetView()中是空值。假如说是item1 滚动出屏幕时会进入到一个叫 RecyclerAndroid构件中去填充 convertView 并缓存起来,当新的项目从屏幕底端上来时,ListView 调用getView()方法,convertView 此时不是空值了,它的值是 item1
  • 你只需要设置新的数据,然后返回 convertView,不必重新创建一个视图 。
    这张图中的西少爷店在刚开业的时候就是借用了复用历史缓存,每天雇佣一批人在那里排队买肉夹馍,然后转到后台把肉夹馍还回去,这样在不知情的人看来就觉得他们家生意特别好,从众心理就会驱使着人们来买他家的东西,老板以前是IT男,所有很会活学活用

2. 缓存item条目的引用–ViewHolder

  • findViewById()这个方法是比较耗性能的操作。当我们通过加载布局文件来创建 View 对象的时候,一但该对象生成,其布局文件中子控件的id 也就不会改变了,因此我们在第一次加载的时候,需要将把相关的数据保存起来,下次直接从缓存中读取,从而减少findViewById()的次数,优化显示效率。
  • 1. 创建一个静态ViewHolder 内部类(可以抽取)
  • 2. 创建自定义的类ViewHolder holder = null ;
  • 3. setTag: 在创建新的 ListView 的时候创建新的 ViewHolder 对象,然后通过 findViewById找到子控件并将其引用保存起来。而 View 中有一个方法 setTag,可用来保存一些数据结构。通过 convertView.setTag(holder)ViewHolder 对象的引用设置到 view 中。
  • 4. 在复用 ListView 中条目的时候,再通过 convertView.getTag(holder)获取 holder对象的引用,此时我们只需要去修改其对象中子控件的值即可,不用 findViewById()
public View getView(final int position, View convertView, ViewGroup parent) {    MyHodler holder = null;    if (view = null){        holder = new MyHodler();        view = View.inflate(context,R.layout.item,null);        holder.tv = (TextView) view.findViewById(R.id.textView);        view.setTag(holder));    } else {        holder = (MyHodler) view.getTag();        holder.tv.setText(list.get(i));    }    return view;}static class MyHodler{    TextView tv;}

二. ListView 中数据的分批及分页加载

  • 当一个应用要展示很多的数据时,一般情况下都不会把所有的数据一次性全部展示出来,通过分页的形式来展示数据。这样会有更好的用户体验。因此,很多应用都是采用分批次加载的形式来获取用户所需的数据。
  • 例如:看手机QQ空间的时候,用户滑动至列表底端时自动加载下一页数据,或者也见到过底部放置一个”查看更多”按钮,用户点击后,加载下一页数据。(服务器需要提供接口, 一次获取一段的数据. 数据库提供接口 ,一次查询一段的数据. )
  • 分页加载的核心技术和大致思路是?
    1. 借助 ListView 的滚动监听器 OnScrollListener,去判断何时该加载新数据(判断是否滚到最后一行)。
    2. 往服务器传递表示页码的参数:page。而该 page 会每加载一屏数据后自动加一。
    3. 利用 addAll()方法不断往 list 集合末端添加新数据,使得适配器的数据源每新加载一屏数据就发生变化。
    4. 利用适配器对象的 notifyDatasetChanged()方法。该方法的作用是通知适配器及与该数据有关的 view,数据已经放生变动,要刷新自己、更新数据。 需要注意的是在 OnScrollListener 监听器中有两个需要实现的方法:屏幕滚动状态发生变化时调用 的方法 onScrollStateChanged()和屏幕滚动时调用的方法 onScroll()。 在滚动状态发生改变的方法中,有三种状态:
    • 1. 手指按下移动的状态: SCROLL_STATE_TOUCH_SCROLL: // 触摸滑动
    • 2. 惯性滑动的状态: SCROLL_STATE_FLING: // 惯性滑翔
    • 3. 静止状态: SCROLL_STATE_IDLE: // 静止

对不同的状态进行处理:

  • 分批加载数据,只关心静止状态,只关心最后一个可见的条目。如果最后一个可见条目就是数据源集合里的最后一个,此时可以加载更多数据。在每次加载的时候,计算出滚动的数量,当滚动的数量大于等于总数量的时候,可以提示用户无更多数据了。

三. 异步加载图片及图片的缓存处理

三级缓存的原理:

  • 先从内存缓存中获取图片显示(内存缓冲)
  • 获取不到的话从 SD 卡里获取(SD 卡缓冲)
  • 都获取不到的话从网络下载图片并保存到 SD 卡同时加入内存
  • 三级缓存的优点:减轻服务器的压力、节省用户的流量开销、提高应用的稳定性和客户端的响应速度,提高用户的体验等等。
  • Tips:优化过程中可能出现的问题:图片重复、错位、闪烁
  • 产生的原因:出现上述的原因是异步加载及对象被复用造成的。
  • 拿图片错位举例:显示错乱是指某行* item* 显示了不属于该行 item 的图片。比如ListView 滑动到第 2 行会异步加载某个图片,但是加载很慢,加载过程中 listView 已经滑动到了第 14 行,第 2 行已不在屏幕内,根据上面介绍的缓存原理,第 2 行的 view 可能被第14 行复用,第 14 行显示了第 2 行的 View,这时之前的图片加载结束,就会显示在第 14 行,造成错乱。
  • 解决办法:给 ImageView 设置一个 Tag, 并预设- 一个图片。这个 Tag 中设置的是图片的 url,然后在加载的时候取得这个 url 和要加载那 position 中的 url 对比,如果不相同就加载,相同就是复用以前的就不加载了。
  • Item2Item14 图片下载的快时, 你滚下去使 Item14 可见,这时 ImageViewtag 被设成了Item14URL, 当 Item2下载完时,由于 Item2 不可见现在的 tagItem14URL,所以不满足条件,虽然下载下来了但不会设置到 ImageView 上, tag 标识的永远是可见 view 中图片的 URL

其它优化:

1.adapter中的getView方法中尽量少使用逻辑
2. 滑动的时候不加载图片
3.ListViewscrollingCache和animateCache设置为false
4. item的布局层级越少越好

总计

  • 最后讲一下我个人对优化的理解。其实对于任何控件或是程序,一提到优化我们主要从两个方面来考虑:
    1.开发者的角度:节省精力物力,提高开发效率
    2.使用者的角度:加载速度更快,运行更流畅,用户体验度更好。这就是我们优化的最终目的。
1 0
原创粉丝点击