Listview性能优化

  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,特别推荐看一下这个文章:https://code.facebook.com/posts/879498888759525/fast-rendering-news-feed-on-android/
  7. 为了保证 ListView 滑动的流畅性,getView() 中要做尽量少的事情,不要有耗时的操作。特别是滑动的时候不要加载图片,停下来再加载,这个库可以帮助你 Glide:https://github.com/bumptech/glide
  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)

时间: 2024-11-17 02:38:09

Listview性能优化的相关文章

Android ListView性能优化实战方案

前言: 对于ListView,大家绝对都不会陌生,只要是做过Android开发的人,哪有不用ListView的呢? 只要是用过ListView的人,哪有不关心对它性能优化的呢? 关于如何对ListView进行性能优化,不仅是面试中常常会被问到的(我前段时间面试了几家公司,全部都问到了这个问题了),而且在实际项目中更是非常重要的一环,它甚至在某种程度上决定了用户是否喜欢接受你的APP.(如果你的列表滑起来很卡,我敢说很多人会直接卸载) 网上关于如何对ListView进行性能优化,提出了很多方案.但

ListView 性能优化------使用ViewHolder,修改layout_weight属性为“match_parent”

转载自:http://blog.csdn.net/pkxiuluo01/article/details/7380860 Adapter是ListView界面与数据之间的桥梁,当列表里的每一项显示到页面时,都会调用Adapter的getView方法返回一个View.如果列表中有很多的项时会占用极大的系统资源,所以我们需要优化Adapter 1.convertView的使用 [java] view plaincopy Java代码 public View getView(int position, 

Android之ListView性能优化——万能适配器

如下图,加入现在有一个这样的需求图,你会怎么做?作为一个初学者,之前我都是直接用SimpleAdapter结合一个Item的布局来实现的,感觉这样实现起来很方便(基本上一行代码就可以实现),而且也没有觉得有什么不好的.直到最近在慕课网上看到鸿洋大神讲的“机器人小慕”和“万能适配器”两节课,才对BaseAdapter有所了解.看了鸿洋大神的课程之后,我又上网搜了几个博客,也看了一些源码和文档,于是打算写一个帖子来记录一下自己的学习历程. 在今天的帖子中,我们从一个最基本的实现BaseAdapter

Android进阶:ListView性能优化异步加载图片 使滑动效果流畅

ListView 是一种可以显示一系列项目并能进行滚动显示的 View,每一行的Item可能包含复杂的结构,可能会从网络上获取icon等的一些图标信息,就现在的网络速度要想保持ListView运行的很好滚动流畅是做不到的 所以这里就需要把这些信息利用多线程实现异步加载 实现这样功能的类 [java] view plaincopy public class AsyncImageLoader { private HashMap<String, SoftReference<Drawable>&

ym——Android之ListView性能优化

Android之ListView性能优化 如果有看过我写过的15k面试题的朋友们一定知道,ListView的优化方式有以下几种: 重用了convertView ViewHolder static class ViewHolder 在列表里面有图片的情况下,监听滑动不加载图片 以上是大致的说了以下,应付面试已经足够了,如果要使用到项目中,可能有些初学者就迷茫了.接下来我详细的说一下,这个是如何优化的. 重用了convertView getView这个方法会调用的次数是你们的数据条目数*2,重用了c

(翻译) Android ListView 性能优化指南

本文翻译了Lucas Rocha的Performance Tips for Android’s ListView.这是一篇关于介绍如何提升ListView性能的文章,非常的优秀.使得我拜读之后,忍不住将其翻译.本文采用了意译的翻译方式,尽可能的保持原文中要表达的内容.但是,任有几处翻译存在一些异议.请读者原谅.如果你对文章的内容有兴趣,请移步到我的blog,地址如下: 地址: http://kohoh1992.github.io/PerformanceTipsForAndroidListView

Android Listview 性能优化

首先我一般使用的适配器是BaseAdapter,其中有两个方法最主要,分别是: getCount,getView, 在对Listview 进行优化的时候,首先使用 convertview 和viewHolder 配合进行优化,使用convertview的母的是 控件复用,从而加到减少内存的使用,使用viewHolder 的是减少findbyid 的次数. 但是在进行控件以后,在进行图片加载的时候,会出现图片错位的问题,这是因为控件里面有上次残留的图片在里面,所以我们在初始化的时候需要设置一张默认

Android之ListView性能优化——使用ConvertView和ViewHolder

使用ConvertView和ViewHolder的优化是针对ListView的Adapter(BaseAdapter)的.这种优化的优点如下: 1)重用了ConveertView,在很大程度上减少了内存的消耗.通过判断ConvertView是否为NULL,如果是NULL那么就需要生成一个新的View出来(通过LayoutInflater生成),绑定数据后显示给用户:如果ConvertView不是NULL,则我们需要做的就只有绑定数据并呈现给用户. 2)由于ListView中的Item往往都是只有

关于ListView性能优化

当listview显示的数据过多,会严重影响性能.这里主要使用的优化方法,就是优化数据适配器(Adapter)的getView方法.尽量减少显示一次数据,重新加载一次布局. getview的加载方法一般有以下三种种方式: (1)最慢的加载方式是每一次都重新定义一个View载入布局,再加载数据 public View getView(int position, View convertView, ViewGroup parent) { View item = mInflater.inflate(R