Listview性能优化

简介: 首先,虽然大家都知道,还是提一下,利用好 convertView 来重用 View,切忌每次 getView() 都新建。ListView 的核心原理就是重用 View。ListView 中有一个回收器,Item 滑出界面的时候 View 会回收到这里,需要显示新的 Item 的时候,就尽量重用回收器里面的 View。
  1. 首先,虽然大家都知道,还是提一下,利用好 convertView 来重用 View,切忌每次 getView() 都新建。ListView 的核心原理就是重用 View。ListView 中有一个回收器,Item 滑出界面的时候 View 会回收到这里,需要显示新的 Item 的时候,就尽量重用回收器里面的 View。
  2. 利用好 View Type,例如你的 ListView 中有几个类型的 Item,需要给每个类型创建不同的 View,这样有利于 ListView 的回收,当然类型不能太多;
  3. 尽量让 ItemView 的 Layout 层次结构简单,这是所有 Layout 都必须遵循的;
  4. 善用自定义 View,自定义 View 可以有效的减小 Layout 的层级,而且对绘制过程可以很好的控制;
  5. 尽量能保证 Adapter 的 hasStableIds() 返回 true,这样在 notifyDataSetChanged() 的时候,如果 id 不变,ListView 将不会重新绘制这个 View,达到优化的目的;
  6. 每个 Item 不能太高,特别是不要超过屏幕的高度,可以参考 Facebook 的优化方法,把特别复杂的 Item 分解成若干小的 Item,特别推荐看一下这个文章:code.facebook.com/posts
  7. 为了保证 ListView 滑动的流畅性,getView() 中要做尽量少的事情,不要有耗时的操作。特别是滑动的时候不要加载图片,停下来再加载,这个库可以帮助你 Glide:github.com/bumptech/gli
  8. 使用 RecycleView 代替。 ListView 每次更新数据都要 notifyDataSetChanged(),有些太暴力了。RecycleView 在性能和可定制性上都有很大的改善,推荐使用。
     
     
 
比如你的Item中有三个按钮,你要为三个按钮分别定义点击事件,如何定义?

也许你会在getView中这样做
  1.  1 button1.setOnclickListener(new View.OnClickListener() {
     2     @override
     3     public void onClick(View v) {
     4         //balabalabala...
     5     }
     6 });
     7 button2.setOnclickListener(new View.OnClickListener() {
     8     @override
     9     public void onClick(View v) {
    10         //balabalabala...
    11     }
    12 });
    13 button3.setOnclickListener(new View.OnClickListener() {
    14     @override
    15     public void onClick(View v) {
    16         //balabalabala...
    17     }
    18 });

     

如果你每屏显示7个Item,你一共创建了21个listener对象在内存中,如果View回收不畅,会更多,这样,在滚动的时候频繁GC 就会导致卡顿(这里描述有误,请看7月25日更新)

如果你在Adapter初始化的时候创建一个Listener
public MyAdapter () {
    myListener = new View.OnClickListener() {
        @override
        public void onClick(View v) {
            v.getTag()
            v.getId()
            //balabalabala...
        }
    });
}

 

通过传入的View v这个参数判断是哪一个button被点击,这样,无论View如何创建,你只创建了1个Listener对象

这只是一个小细节,优化的方式要综合使用,才会事半功倍


------------------7月24日更-----------------

v.getTag() 这个tag本不是用来存数据的,通俗点讲它和view 的Id是同一个东西,只不过tag的类型是Object。实际上在tag中存储数据是不符合规范的方式

但其实View类有两种tag,
setTag ( Object  tag) 方法将tag保存在一个成员变量中,findViewWithTag正是遍历此tag

setTag (int key,  Object  tag) 方法是最终是调用View类中的setKeyedTag(int key, Object tag)
这是一个私有方法
他是用 SparseArray 实现的,我们可以把需要的东西存到这里面(其实观察源码可以发现,系统很多时候都是这样做的)

这里要注意一点,参数key必须是唯一的,那么我们可以这样做
那么需要先在res/values/strings.xml中添加
  1. <resources>
    <item type="id" name="tag_first"></item>
    <item type="id" name="tag_second"></item>
    </resources>

     

  2. 使用的时候写成
  3. view.setTag(R.id.tag_first, obj1);
    view.setTag(R.id.tag_second, obj2);

     

-------------------------关于如何绑定数据的问题-----------------

有人问,如果这样写,所有button只能通过id区分逻辑,无法传入每个item的数据

我们可以将数据通过view 的tag带进来
1 public View getView(.....) {
2 ....
3 v.setTag(key, getItem(position));
4 ....
5 }
然后在listener中通过v.getTag()将数据取出
-----------------------7月25日update-------------------
这里我有一个失误
如果listener里面的逻辑与当前的item有关,那么其实并不只是创建了21个listener对象
 1 public void getView (View convertView ,final int position ....) {
 2     if (convertView == null) {
 3         View v = LayoutInflater.from(mContext).inflate(...);
 4         v.setOnclickListener(new View.OnClickListener () {
 5             @override
 6             public void onClick(View v) {
 7                 getItem(position);
 8             }
 9         });
10     } else {
11                
12     }
13 }
你看下,如果绑定数据在convertView为空的情况下确实只创建了有限个listener,
但是在这种情况下绑定上的数据只有View创建时的7个,之后不为空的情况下没有更换listener导致重用的view数据是新的,listener里面的position依然是过去创建view时的7个之一,不会变化(注意参数上的final)
若在else里面再重新setListener,view是有重用,listener被换成新的,并与新的position绑定,老的listener就会变成一个废对象,等待gc回收,随着list滚动,越来越多
关键是我们的业务与position这个参数有关

1.重用 convertView 
用以避免重复创建 View,重复创建 View 代价较大,而且如果重用 view 不改变宽高,重用View可以减少重新分配缓存造成的内存频繁分配/回收;

2. 避免在 getView 中有 重复调用的 findViewById
findViewById 的实现是遍历,如果你定义的 View 越复杂代价越大。
Google 推荐的做法是用 ViewHolder,然后保存在 view 的 tag 中。现在 RecyclerView 也是强制使用 ViewHolder 了。

3. 设置 View (如 TextView#setText )之前先对比数据是否有改变
一般来说,【比较两个数据的代价】远小于【 View 的重绘的代价】

4. 避免在 getView 函数中直接加载 Image 或做其他比较耗时的操作
加载本地 Image 需要载入内存以及解析 Bitmap ,都是比较耗时的操作。
用户快速滑动列表时,会大量调用 getView ,而 getView 是在主线程中被调用的。如果你在 getView 函数中直接加载 Image 或做其他耗时操作,就会造成滑动比较卡。加载 ImageView 的解决方案就是开一个线程去把做这事。有很多第三库可以做这事。

5. ListView 中元素避免半透明
半透明绘制需要大量乘法计算,在滑动时不停重绘会造成大量的计算,在比较差的机子上会比较卡。在设计上能不半透明就不不半透明。实在要弄的话我个人是用个比较偷懒的方法,是在滑动的时候把半透明设置成不透明,滑动完再重新设置成半透明。

6. 尽量开启硬件加速
硬件加速提升巨大,避免使用一些不支持的函数导致含泪关闭某个地方的硬件加速。
当然这一条不只是对 ListView。

7.用 ListView 威力加强版 -- RecyclerView
更多的新武将,更多的姿势,更规范的使用,更好用的动画,更加强大的变化
 
转载自知乎(http://www.zhihu.com/question/19703384
相关文章
|
3月前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
68 5
|
9月前
|
缓存 移动开发 Android开发
Android 应用性能优化实践
【5月更文挑战第15天】 在移动开发领域,应用的性能直接关系到用户体验和产品的市场表现。特别是对于安卓平台,设备的多样性和应用生态环境的复杂性使得性能优化成为开发者的一项重要技能。本文将深入探讨针对安卓应用的性能瓶颈识别、分析方法以及具体的优化策略,旨在为开发者提供一套实用的性能提升解决方案。
|
9月前
|
缓存 监控 Android开发
Android 应用性能优化实战
【4月更文挑战第27天】 在竞争激烈的移动应用市场中,性能优越的应用更能吸引和保留用户。针对Android平台,本文将深入探讨影响应用性能的关键因素,并提供一系列实用的优化策略。我们将从内存管理、UI渲染、多线程处理以及电池使用效率等方面入手,通过具体案例分析如何诊断常见问题,并给出相应的解决方案。文中所提技巧旨在帮助开发者构建更加流畅、高效的Android应用。
87 2
|
XML 存储 Android开发
ListView的“终极优化”,打造你的万能适配器
传统的代码逻辑我们是怎么使用的呢?每写一个ListView,都要去写一个Adapter类,一个ViewHolder类,这几乎是我们必须要操作的,以致于有太多太多的冗余代码,让我们感到真的不厌其烦,一个两个还可以,十个八个,就真的有点太崩溃了,不仅代码繁琐,还会占用内存,为了解决这样的一个问题,下面就要开始对其抽取优化。
|
存储 缓存 Java
RecyclerView 性能优化 | 是什么在破坏缓存机制?
RecyclerView 性能优化 | 是什么在破坏缓存机制?
201 0
|
存储 XML JavaScript
Android 性能优化篇之SharedPreferences使用优化
`SharedPreferences(以下简称SP)`是Android本地存储的一种方式,是以`key-value`的形式存储在`/data/data/项目包名/shared_prefs/sp_name.xml`里
424 0
|
缓存 算法 API
使用优化 | RecyclerView中可优化的点
使用优化 | RecyclerView中可优化的点
使用优化 | RecyclerView中可优化的点
|
Android开发
Webview性能优化-小结
Webview性能优化-小结
762 0
|
缓存 API 数据库
UITableView性能优化分析总结
UITableView是iOS中使用最频繁的控件之一,其性能优化是我们经常要面对的,尤其是当数据量偏大并且设备性能不足时。
311 0
|
Android开发
listview加载性能优化之view的复用
listview加载性能优化之view的复用