Android-ListView优化常见的三种方式
2015-08-09 20:59
567 查看
优化原理
使用ListView时尽可能的少去执行Layout的Inflate,只渲染和布置那些在可视范围内,或者即将出现在可视范围内的Item
第一
Layout的Inflate是消耗资源巨大的代码。即使,Layout文件已经被高效的解析程序转换为了二进制代码。Infalte操作依旧需要彻底包含整个XML代码树,而且还要实例化相应的View。在Android 的源码中,ListView通过View回收机制解决了这个问题。这样就可以非常简单的通过可回收的View设置每个Item的内容。而不用,为每一个Item都Inflate Layout 。
第二
通过ListView的View回收机制。在可视范围上面或者下面的View加入到回收池中。当在可视范围内的View被移出可视范围内时,其也会被添加到回收池中。以这种方式ListView只需占用非常少的内存几可以存储可视范围内的View和回收池中的View。ListView的View回收机制以两种不同的方式提供可回收的View即从上往下提供,和从下往上提供。采取何种方式,取决于滑动的方式。下面这张图展示了,当你下滑时ListView的View回收机制所做的工作。
![](https://img-blog.csdn.net/20150809205925708?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
当滑动ListView时,ListView动态的提供可回收的View。因此,如何使Adapter的getView()变得尽可能轻巧成为了关键所在。
方式一:使用可回收的View
每当ListView需要显示一条新的Item的时候,它会回调你的Adapter的getView()方法。getView()方法有三个参数:Item的位置,convertView,Item的上级容器。
参数convertView实际上就是一个之前提到的可回收的View。当ListView要回收这个View的时候,它的数据就会被清空。因此,当convertView不为null的时候,只需要将数据填充到里面,而不用Inflate一个新的View。
Adapter中的getView()方法的代码应该像下面这样设计:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.your_layout, null);
}
TextView text = (TextView) convertView.findViewById(R.id.text);
text.setText("Position " + position);
return convertView;
}
方式二:使用ViewHolder
在一个已经Inflate的Layout中寻找View是Android开发中非常普遍的操作。这通常通过View的findViewById方法来实现。这个方法会递归整个View树,以寻找那个与ID匹配的View。在静态的代码中使用findViewById()还是非常棒的,但是,如你看到的那样。当滑动ListView的时候,ListView会非常频繁的回调Adapter的getView()方法。这就可能在不知不觉中影响ListView的滑动性能。尤其发生在你的Item的Layout非常的复杂的时候。
ViewHolder就是用来存储那些在你的getView()方法中调用findViewById()方法得到的View。ViewHolder是一个非常轻巧的内部类。它存储那些Item内部的View的直接引用。然后可以在Inflate结束之后,将ViewHolder对象存储在Item的tag当中。以这种方式,你只需要在第一次创建Item的时候调用findViewById就可以了。
下面就是使用ViewHolder提高ListView的代码:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.your_layout, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {
holder = convertView.getTag();
}
holder.text.setText("Position " + position);
return convertView;
}
private static class ViewHolder {
public TextView text;
}
方式三:使用异步加载
在Android的App中,ListView使用像图片这样耗费资源的Item是非常普遍的。在你的Adapter中使用drawable资源是非常棒的,因为Android内存的代码会缓存这些资源。但是,你可能会想要使用更加灵活的内容。比如来自本地设备或者来自网络的缩略图或者图片之类的。在这种情况下,你可能不想要直接的在你的Adapter的getView()中加载这些资源。因为阻塞UI线程会造成ANR异常。同时这么做也使得你的ListView滑动的更加平滑。
你所要做的事情就是让那些需要IO操作或者耗费CPU资源的操作在一个额外的线程中运行。为了实现这个目的,你任然需要遵循ListView的View回收机制的规则。例如:当你的Adapter的getView()正在使用AsyncTask加载一张图片。需要这张图片的Item可能已经在图片加载完成之前就已经被ListView的View回收机制回收了。因此你必须要知道,当你完成异步加载的时候,对应的Item是否已经被回收了。
一个简单的方法是,为你的AsyncTask提供一个识别信息用以区别其对应的Item。这样,当你的AsyncTask完成加载工作的时候,就可以判断对应的Item还是不式最初的那个Item。事实上存在许多方式实现这个功能,下面的代码只是其中最为简单的一种:
public View getView(int position, View convertView,
ViewGroup parent) {
ViewHolder holder;
...
holder.position = position;
new ThumbnailTask(position, holder)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);
return convertView;
}
private static class ThumbnailTask extends AsyncTask {
private int mPosition;
private ViewHolder mHolder;
public ThumbnailTask(int position, ViewHolder holder) {
mPosition = position;
mHolder = holder;
}
@Override
protected Cursor doInBackground(Void... arg0) {
// Download bitmap here
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (mHolder.position == mPosition) {
mHolder.thumbnail.setImageBitmap(bitmap);
}
}
}
private static class ViewHolder {
public ImageView thumbnail;
public int position;
}
使用ListView时尽可能的少去执行Layout的Inflate,只渲染和布置那些在可视范围内,或者即将出现在可视范围内的Item
第一
Layout的Inflate是消耗资源巨大的代码。即使,Layout文件已经被高效的解析程序转换为了二进制代码。Infalte操作依旧需要彻底包含整个XML代码树,而且还要实例化相应的View。在Android 的源码中,ListView通过View回收机制解决了这个问题。这样就可以非常简单的通过可回收的View设置每个Item的内容。而不用,为每一个Item都Inflate Layout 。
第二
通过ListView的View回收机制。在可视范围上面或者下面的View加入到回收池中。当在可视范围内的View被移出可视范围内时,其也会被添加到回收池中。以这种方式ListView只需占用非常少的内存几可以存储可视范围内的View和回收池中的View。ListView的View回收机制以两种不同的方式提供可回收的View即从上往下提供,和从下往上提供。采取何种方式,取决于滑动的方式。下面这张图展示了,当你下滑时ListView的View回收机制所做的工作。
当滑动ListView时,ListView动态的提供可回收的View。因此,如何使Adapter的getView()变得尽可能轻巧成为了关键所在。
方式一:使用可回收的View
每当ListView需要显示一条新的Item的时候,它会回调你的Adapter的getView()方法。getView()方法有三个参数:Item的位置,convertView,Item的上级容器。
参数convertView实际上就是一个之前提到的可回收的View。当ListView要回收这个View的时候,它的数据就会被清空。因此,当convertView不为null的时候,只需要将数据填充到里面,而不用Inflate一个新的View。
Adapter中的getView()方法的代码应该像下面这样设计:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.your_layout, null);
}
TextView text = (TextView) convertView.findViewById(R.id.text);
text.setText("Position " + position);
return convertView;
}
方式二:使用ViewHolder
在一个已经Inflate的Layout中寻找View是Android开发中非常普遍的操作。这通常通过View的findViewById方法来实现。这个方法会递归整个View树,以寻找那个与ID匹配的View。在静态的代码中使用findViewById()还是非常棒的,但是,如你看到的那样。当滑动ListView的时候,ListView会非常频繁的回调Adapter的getView()方法。这就可能在不知不觉中影响ListView的滑动性能。尤其发生在你的Item的Layout非常的复杂的时候。
ViewHolder就是用来存储那些在你的getView()方法中调用findViewById()方法得到的View。ViewHolder是一个非常轻巧的内部类。它存储那些Item内部的View的直接引用。然后可以在Inflate结束之后,将ViewHolder对象存储在Item的tag当中。以这种方式,你只需要在第一次创建Item的时候调用findViewById就可以了。
下面就是使用ViewHolder提高ListView的代码:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.your_layout, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {
holder = convertView.getTag();
}
holder.text.setText("Position " + position);
return convertView;
}
private static class ViewHolder {
public TextView text;
}
方式三:使用异步加载
在Android的App中,ListView使用像图片这样耗费资源的Item是非常普遍的。在你的Adapter中使用drawable资源是非常棒的,因为Android内存的代码会缓存这些资源。但是,你可能会想要使用更加灵活的内容。比如来自本地设备或者来自网络的缩略图或者图片之类的。在这种情况下,你可能不想要直接的在你的Adapter的getView()中加载这些资源。因为阻塞UI线程会造成ANR异常。同时这么做也使得你的ListView滑动的更加平滑。
你所要做的事情就是让那些需要IO操作或者耗费CPU资源的操作在一个额外的线程中运行。为了实现这个目的,你任然需要遵循ListView的View回收机制的规则。例如:当你的Adapter的getView()正在使用AsyncTask加载一张图片。需要这张图片的Item可能已经在图片加载完成之前就已经被ListView的View回收机制回收了。因此你必须要知道,当你完成异步加载的时候,对应的Item是否已经被回收了。
一个简单的方法是,为你的AsyncTask提供一个识别信息用以区别其对应的Item。这样,当你的AsyncTask完成加载工作的时候,就可以判断对应的Item还是不式最初的那个Item。事实上存在许多方式实现这个功能,下面的代码只是其中最为简单的一种:
public View getView(int position, View convertView,
ViewGroup parent) {
ViewHolder holder;
...
holder.position = position;
new ThumbnailTask(position, holder)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);
return convertView;
}
private static class ThumbnailTask extends AsyncTask {
private int mPosition;
private ViewHolder mHolder;
public ThumbnailTask(int position, ViewHolder holder) {
mPosition = position;
mHolder = holder;
}
@Override
protected Cursor doInBackground(Void... arg0) {
// Download bitmap here
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (mHolder.position == mPosition) {
mHolder.thumbnail.setImageBitmap(bitmap);
}
}
}
private static class ViewHolder {
public ImageView thumbnail;
public int position;
}
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories