ListView优化 思路及原理

最佳实践

1、将【内部类】用 static修饰一下。public static class ViewHolder { }

2、将实例mViewHolder作为外部类的成员变量。private ViewHolder mViewHolder;

注解:

  • ViewHolder是否用static修饰,结果都是一样的,效率也没什么提升,不过大家都习惯用 static修饰一下。
  • 一种情况下可以将mViewHolder作为局部变量,那就是想【临时】操作屏幕上item中的控件,这时屏幕上的mViewHolder和convertView是一一对应的,可以方便的通过mViewHolder获取你所操作的convertView中的所有控件,进而进行各种操作。但是一定要谨记,这种操作是临时的,下次进入界面时又会恢复原来的状态。要实现永久存储,只有一个办法,那就是将你所操作的结果保存在listview的adapter中的集合中,既然是操作集合,那肯定是通过point获取、操作控件了。
  • 所以,根据需求,若是想临时修改控件属性,就将mViewHolder作为局部变量,若是想持久修改控件属性,就采取将状态保存在集合中的方式。

View重用的原理探讨

1. View的重用的原理

  • View的每次创建是比较耗时的,因此对于 getview 方法传入的 convertView 应充分利用 != null 的判断。
  • 如果你有N个 item,其中只有可见的 item +1 个(即 item1 - item8 )存在内存中,其他的在 Recycler(循环器)中。
  • ListView先一个一个的调用 getView 创建所有可见的 item 的视图,此时getView中的参数 convertView 是空 null 的。
  • 当 item1 滚出屏幕,并且一个新的 item8 从屏幕底端滚上来时(只要有一点点露头就算滚上来了),ListView再调用 getView 请求一个 item9 的视图时,convertView 不是空值了,它的值就是刚刚滚出去的 item1。
  • 所以这时你只需将 item9 的【数据】赋给 convertView 然后返回 convertView 即可得到 item9 的视图,而不必重新 new 一个视图。

2、ViewHolder的应用

  • View的 findViewById 方法也是比较耗时的,因此需要考虑只调用一次,之后就用 View.getTag() 方法来获得ViewHolder对象。我们知道,每个item都有同样的【视图结构】,item之间仅仅是所填充的数据不同,若每次都为得到一个相同结构的视图都要重新 inflate 布局文件,然后 findViewById 布局中的控件,这是很浪费资源的,为了不重复 findViewById,我们可以定义一个 ViewHolder 用于封装所有需要更新数据的控件,那么以后就可以直接赋值。
  • ViewHolder只是将需要缓存的那些view封装好,convertView的setTag才是将这些缓存起来供下次调用。View中的 setTag 方法表示给View添加一个额外的数据,以后可以用getTag()将这个数据取出来。

代码

现在大家都知道用ViewHolder来实现listview的优化了,但是,ViewHolder到底要用什么来修饰,ViewHolder的实例到底应该放在哪呢?为了弄清楚这个问题,我做了以下测试。
我简单说一下代码是什么意思,ViewHolder有一个成员变量id,用来区分不同的ViewHolder,在构造函数中,对id进行赋值,itemId是一个静态变量,每初始化一次就+1,我们可以根据构造函数的打印次数,来计算ViewHolder的实例化次数,根据toString()可以来判断到底是使用了哪一个ViewHolder。getVIew中的写法是固定的,下面是测试结果:

public class MainActivity extends ListActivity {

//ViewHolder mViewHolder;

//作为成员变量,首先调用10次 getView(),并且每调用一次,就对【此实例】重新初始化一次(new)

//虽然new了10次,但至始自终都是指向的是同一个地址,并且这个ViewHolder 始终只代表【最后出现】的那个条目

public static int itemId = 1;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

getListView().setAdapter(new MyAdapter());

}

private class MyAdapter extends BaseAdapter {

@Override

public int getCount() {

return 100;

}

@Override

public Object getItem(int position) {

return position;

}

@Override

public long getItemId(int position) {

return position;

}

@Override

public View getView(int position, View convertView, ViewGroup parent) {

final ViewHolder mViewHolder; //或者ViewHolder mViewHolder;

//若作为局部变量,初始化时会先调用10次 getView(),每调用一次,就 会new一个新的 ViewHolder

//所以共有10个 ViewHolder 实例,每个 ViewHolder 实例代表一个【当前屏幕】上的一个条目

//注意:final修饰引用类型变量时,只是保证这个引用的地址不变,即一直引用同一对象,但这个对象自身是可以改变的

if (convertView == null) {

convertView = getLayoutInflater().inflate(R.layout.item, parent, false);

mViewHolder = new ViewHolder();

mViewHolder.tv = (TextView) convertView.findViewById(R.id.tv);

convertView.setTag(mViewHolder);//标记

} else mViewHolder = (ViewHolder) convertView.getTag();

mViewHolder.tv.setText("listview条目的序号=" + position + ",mViewHolder的id=" + mViewHolder.id);

//不管采用的是哪种方法,在次操作item中的控件都没有任何问题!千万别忧虑!

Log.d("bqt", "position=" + position + "    " + mViewHolder.id + "    " + mViewHolder.hashCode());

return convertView;

}

}

private class ViewHolder {//ViewHolder用private、private static、private final或private static final修饰时完全一样!

//结论:如果界面内可见的item是9个,那么不管哪种方式,自始至终ViewHolder一共初始化了9+1次

//之后都是复用之前初始化的ViewHolder,但并不能保证哪个item复用的是哪个ViewHolder的实例。

private int id;//内部类的任何成员都是完全暴露给外部类的

private TextView tv;

private ViewHolder() {

id = itemId;

itemId++;

}

}

}

item的布局

item.xml

<?xml version="1.0" encoding="utf-8"?>

<TextView xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/tv"

android:layout_width="match_parent"

android:layout_height="62dp"

android:gravity="center_vertical"

android:text="listview的每个条目" />

实验一,如何修饰内部类ViewHolder

结论1:如果界面内可见的item数量是9个,那么自始至终ViewHolder一共初始化了9+1次,之后都是复用ViewHolder,但并不保证哪个item复用的是哪个ViewHolder的实例。

结论2:内部类 ViewHolder 不管是用 private 修饰,还是用 private static 修饰,还是用 private final 修饰,还是用 private static final class ViewHolder修饰,完全一样!

static修饰类,在这里是静态内部类,并不是说只存在一个实例,而是可以访问外部类的静态变量,final修饰类则是不让该类继承,我们这里使用final毫无根据,所以,以后写ViewHolder的时候,可以不纠结了,加什么加啊,什么都不用加!

          

实验二,ViewHolder的实例放在哪里

结论:

mViewHolder无论是作为activity的成员变量,还是作为getView()的局部变量,还是作为getView()的 final 局部变量,当然mViewHolder不能作为final的成员变量,上面的结论依然成立。

个人理解的区别:

  • 1、作为成员变量,首先调用10次 getView(),那么每调用一次,就初始化一次,虽然 new 了10次,但至始自终只有一个 ViewHolder 实例,并且这个ViewHolder 始终只代表最后出现的那个条目
  • 2、作为局部变量,首先调用10次 getView(),很明显,每调用一次,就 new了一个新的 ViewHolder,所以共有10个 ViewHolder 实例,每个 ViewHolder 实例代表一个当前屏幕上的一个条目
  • 3、所以,主要区别在于,作为成员变量,只有一个对象,始终代表最后出现的item;作为局部变量,共有10个对象,代表屏幕上的每个item。

来自为知笔记(Wiz)

时间: 2024-11-06 23:39:13

ListView优化 思路及原理的相关文章

Android 复杂布局的ListView优化思路

项目中用到一个内部复杂布局的listview,每个item中都有动态的子item,相当于listview的item中还有listview的样式.刚开始做的思路是,用一个listview,然后item中加个LinearLayout,然后代码动态的生成子item view 添加在其中,希望这样的表述大家能明白,按照这样的思路做好之后发现快速滑动的时候,一卡一顿,实在不能直视,在配置低点的机器上更是不能看. 但是因为赶进度一直没有优化,现在回头看下,虽然已经用了viewHolder但是listview

Join的实现原理及优化思路

前言 前面我们已经了解了MySQLQueryOptimizer的工作原理,学习了Query优化的基本原则和思路,理解了索引选择的技巧,这一节我们将围绕Query语句中使用非常频繁,且随时可能存在性能隐患的Join语句,继续我们的Query优化之旅. Join 的实现原理 在寻找Join语句的优化思路之前,我们首先要理解在MySQL中是如何来实现Join的,只要理解了实现原理之后,优化就比较简单了.下面我们先分析一下MySQL中Join的实现原理. 在MySQL中,只有一种Join算法,就是大名鼎

转 Join的实现原理及优化思路

前言 前面我们已经了解了MySQLQueryOptimizer的工作原理,学习了Query优化的基本原则和思路,理解了索引选择的技巧,这一节我们将围绕Query语句中使用非常频繁,且随时可能存在性能隐患的Join语句,继续我们的Query优化之旅. Join 的实现原理 在寻找Join语句的优化思路之前,我们首先要理解在MySQL中是如何来实现Join的,只要理解了实现原理之后,优化就比较简单了.下面我们先分析一下MySQL中Join的实现原理. 在MySQL中,只有一种Join算法,就是大名鼎

MySQL join的实现原理及优化思路

Join 的实现原理 在MySQL 中,只有一种Join 算法,也就是Nested Loop Join,没有其他很多数据库所提供的Hash Join,也没有Sort Merge Join.顾名思义,Nested Loop Join 实际上就是通过驱动表的结果集作为循环基础数据,然后一条一条的通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果.如果还有第三个参与Join,则再通过前两个表的Join 结果集作为循环基础数据,再一次通过循环查询条件到第三个表中查询数据,如此往复. 下面

10熟练掌握listview优化

熟练掌握listview优化,获取网络图片异步加载,分批加载,分页显示,图片缓存等优化方式 ListView的工作原理 首先来了解一下ListView的工作原理(可参见http://mobile.51cto.com/abased-410889.htm),如图: ListView 针对每个item,要求 adapter "返回一个视图" (getView),也就是说ListView在开始绘制的时候,系统首先调用getCount()函数,根据他的返回值得到ListView的长度,然后根据这

listView优化以及内存泄露问题

最经开发app使出现了由于ListView产生的内存泄露问题.我们知道内存泄露时很不好的.意味着,代码写的有点失败,需要做些优化改动. 经过这次的教训,以及在网上找了些资料,总结了一下,关于ListView的优化: listview优化问题: 首先,listview必须严格按照convertView及viewHolder格式书写,这样可以基本保证数据最优. 其次,如果自定义Item中有涉及到图片等等的,一定要做图片优化.bitmap释放可以不做. 第三,尽量避免在BaseAdapter中使用st

ListView优化分页优化

缘由 我们在用ListView展现数据的时候.比如展现联系人,如果联系人太多就会出现卡的现象,比如如果有1000多条数据,从数据库里查询,然后装载到List容器这段时间是比较耗时的.虽然我们可以用asyncTask来单独开启一个子线程加载.一次查看那么多,未免有点多余.是否可以通过先装载30条数据,如果用户需要我们在继续查询并且展示后面的数据,这样一来可以提升.使用速率. 实现方法 1.数据库分页查询 首先写SqlHelper指定每次查询数据库多少数据.代码如下 /*** 分页查询黑名单* *

Adaptert Listview 优化

这次是关于Listview的优化的,之前一直采用愚蠢的方式来使用listview,出现的情况就是数据多的话下拉的时候会出现卡顿的情况,内存占用多.所以学习了关于listview的优化,并且这也是普遍使用的方法.我们一般来说创建listview的话更常用的是继承BaseAdapter的方式进行实现,因为这样可以自定义list没一个item的布局,根据程序的需求进行定制,一般来说listview所展示的数据都是比较多的,一般也有比较少的.所以,不对adapter进行优化的话,那么每一次拖动listv

Android ListView动画特效实现原理及源码

Android 动画分三种,其中属性动画为我们最常用动画,且能满足项目中开发几乎全部需求,google官方包支持3.0+,我们可以引用三方包nineoldandroids来失陪到低版本.本例子中就是用属性动画实现效果. 对普通的View做动画,我们只要定义好要的动画ObjectAnimator或AnimatorSet,然后设置属性启动及可.但是,对ListView做动画应该如何.什么时候.在什么地方.对哪个View做动画属性呢? github上有成熟的listview动画包 https://gi