ListView之性能优化
listview加载的核心是其adapter,本文通过减少adapter中创建、处理view的次数来提高listview加载的性能,总共分四个层次:
0、最原始的加载
1、利用convertView
2、利用ViewHolder
3、实现局部刷新
〇、最原始的加载
这里是不经任何优化的adapter,为了看起来方便,把listview的数据直接在构造函数里传给adapter了,代码如下:
private class AdapterOptmL0 extends BaseAdapter {
private LayoutInflater mLayoutInflater;
private ArrayList<Integer> mListData; public AdapterOptmL0(Context context, ArrayList<Integer> data) {
mLayoutInflater = LayoutInflater.from(context);
mListData = data;
} @Override
public int getCount() {
return mListData == null ? 0 : mListData.size();
} @Override
public Object getItem(int position) {
return mListData == null ? 0 : mListData.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
View viewRoot = mLayoutInflater.inflate(R.layout.listitem, parent, false);
if (viewRoot != null) {
TextView txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
txt.setText(getItem(position) + "");
}
return viewRoot;
}
}
[转载请保留本文地址:http://www.cnblogs.com/snser/p/5158064.html]
一、利用convertView
上述代码的第27行在Eclipse中已经提示警告:
Unconditional layout inflation from view adapter: Should use View Holder pattern (use recycled view passed into this method as the second parameter) for smoother scrolling
这个意思就是说,被移出可视区域的view是可以回收复用的,它作为getview的第二个参数已经传进来了,所以没必要每次都从xml里inflate。
经过优化后的代码如下:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
}
if (convertView != null) {
TextView txt = (TextView)convertView.findViewById(R.id.listitem_txt);
txt.setVisibility(View.VISIBLE);
txt.setText(getItem(position) + "");
}
return convertView;
}
上述代码加了判断,如果传入的convertView不为null,则直接复用,否则才会从xml里inflate。
按照上述代码,如果手机一屏最多同时显示5个listitem,则最多需要从xml里inflate 5 次,比AdapterOptmL0中每个listitem都需要inflate显然效率高多了。
上述的用法虽然提高了效率,但带来了一个陷阱,如果复用convertView,则需要重置该view所有可能被修改过的属性。
举个例子:
如果第一个view中的textview在getview中被设置成INVISIBLE了,而现在第一个view在滚动过程中出可视区域,并假设它作为参数传入第十个view的getview而被复用
那么,在第十个view的getview里面不仅要setText,还要重新setVisibility,因为这个被复用的view当前处于INVISIBLE状态!
[转载请保留本文地址:http://www.cnblogs.com/snser/p/5158064.html]
二、利用ViewHolder
从AdapterOptmL0第27行的警告中,我们还可以看到编译器推荐了一种模型叫ViewHolder,这是个什么东西呢,先看代码:
private class AdapterOptmL2 extends BaseAdapter {
private LayoutInflater mLayoutInflater;
private ArrayList<Integer> mListData; public AdapterOptmL2(Context context, ArrayList<Integer> data) {
mLayoutInflater = LayoutInflater.from(context);
mListData = data;
} private class ViewHolder {
public ViewHolder(View viewRoot) {
txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
}
public TextView txt;
} @Override
public int getCount() {
return mListData == null ? 0 : mListData.size();
} @Override
public Object getItem(int position) {
return mListData == null ? 0 : mListData.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
ViewHolder holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
if (convertView != null && convertView.getTag() instanceof ViewHolder) {
ViewHolder holder = (ViewHolder)convertView.getTag();
holder.txt.setVisibility(View.VISIBLE);
holder.txt.setText(getItem(position) + "");
}
return convertView;
}
}
从代码中可以看到,这一步做的优化是用一个类ViewHolder来保存listitem里面所有找到的子控件,这样就不用每次都通过耗时的findViewById操作了。
这一步的优化,在listitem布局越复杂的时候效果越为明显。
[转载请保留本文地址:http://www.cnblogs.com/snser/p/5158064.html]
三、实现局部刷新
OK,到目前为止,listview普遍需要的优化已经做的差不多了,那就该考虑实际使用场景中的优化需求了。
实际使用listview过程中,通常会在后台更新listview的数据,然后调用Adatper的notifyDataSetChanged方法来更新listview的UI。
那么问题来了,一般情况下,一次只会更新listview的一条/几条数据,而调用notifyDataSetChanged方法则会把所有可视范围内的listitem都刷新一遍,这是不科学的!
所以,进一步优化的空间在于,局部刷新listview,话不多说见代码:
private class AdapterOptmL3 extends BaseAdapter {
private LayoutInflater mLayoutInflater;
private ListView mListView;
private ArrayList<Integer> mListData; public AdapterOptmL3(Context context, ListView listview, ArrayList<Integer> data) {
mLayoutInflater = LayoutInflater.from(context);
mListView = listview;
mListData = data;
} private class ViewHolder {
public ViewHolder(View viewRoot) {
txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
}
public TextView txt;
} @Override
public int getCount() {
return mListData == null ? 0 : mListData.size();
} @Override
public Object getItem(int position) {
return mListData == null ? 0 : mListData.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
ViewHolder holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
if (convertView != null && convertView.getTag() instanceof ViewHolder) {
updateView((ViewHolder)convertView.getTag(), (Integer)getItem(position));
}
return convertView;
} public void updateView(ViewHolder holder, Integer data) {
if (holder != null && data != null) {
holder.txt.setVisibility(View.VISIBLE);
holder.txt.setText(data + "");
}
} public void notifyDataSetChanged(int position) {
final int firstVisiablePosition = mListView.getFirstVisiblePosition();
final int lastVisiablePosition = mListView.getLastVisiblePosition();
final int relativePosition = position - firstVisiablePosition;
if (position >= firstVisiablePosition && position <= lastVisiablePosition) {
updateView((ViewHolder)mListView.getChildAt(relativePosition).getTag(), (Integer)getItem(position));
} else {
//不在可视范围内的listitem不需要手动刷新,等其可见时会通过getView自动刷新
}
}
}
修改后的Adapter新增了一个方法 public void notifyDataSetChanged(int position) 可以根据position只更新指定的listitem。
[转载请保留本文地址:http://www.cnblogs.com/snser/p/5158064.html]
局部刷新番外篇
在局部刷新数据的接口中,实际上还可以再干点事情:listview正在滚动的时候不去刷新。
具体的思路是,如果当前正在滚动,则记住一个pending任务,等listview停止滚动的时候再去刷,这样不会造成滚动的时候刷新错乱。代码如下:
private class AdapterOptmL3Plus extends BaseAdapter implements OnScrollListener{
private LayoutInflater mLayoutInflater;
private ListView mListView;
private ArrayList<Integer> mListData; private int mScrollState = SCROLL_STATE_IDLE;
private List<Runnable> mPendingNotify = new ArrayList<Runnable>(); public AdapterOptmL3Plus(Context context, ListView listview, ArrayList<Integer> data) {
mLayoutInflater = LayoutInflater.from(context);
mListView = listview;
mListData = data;
mListView.setOnScrollListener(this);
} private class ViewHolder {
public ViewHolder(View viewRoot) {
txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
}
public TextView txt;
} @Override
public int getCount() {
return mListData == null ? 0 : mListData.size();
} @Override
public Object getItem(int position) {
return mListData == null ? 0 : mListData.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
ViewHolder holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
if (convertView != null && convertView.getTag() instanceof ViewHolder) {
updateView((ViewHolder)convertView.getTag(), (Integer)getItem(position));
}
return convertView;
} public void updateView(ViewHolder holder, Integer data) {
if (holder != null && data != null) {
holder.txt.setVisibility(View.VISIBLE);
holder.txt.setText(data + "");
}
} public void notifyDataSetChanged(final int position) {
final Runnable runnable = new Runnable() {
@Override
public void run() {
final int firstVisiablePosition = mListView.getFirstVisiblePosition();
final int lastVisiablePosition = mListView.getLastVisiblePosition();
final int relativePosition = position - firstVisiablePosition;
if (position >= firstVisiablePosition && position <= lastVisiablePosition) {
if (mScrollState == SCROLL_STATE_IDLE) {
//当前不在滚动,立刻刷新
Log.d("Snser", "notifyDataSetChanged position=" + position + " update now");
updateView((ViewHolder)mListView.getChildAt(relativePosition).getTag(), (Integer)getItem(position));
} else {
synchronized (mPendingNotify) {
//当前正在滚动,等滚动停止再刷新
Log.d("Snser", "notifyDataSetChanged position=" + position + " update pending");
mPendingNotify.add(this);
}
}
} else {
//不在可视范围内的listitem不需要手动刷新,等其可见时会通过getView自动刷新
Log.d("Snser", "notifyDataSetChanged position=" + position + " update skip");
}
}
};
runnable.run();
} @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
mScrollState = scrollState;
if (mScrollState == SCROLL_STATE_IDLE) {
//滚动已停止,把需要刷新的listitem都刷新一下
synchronized (mPendingNotify) {
final Iterator<Runnable> iter = mPendingNotify.iterator();
while (iter.hasNext()) {
iter.next().run();
iter.remove();
}
}
}
} @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
}
[转载请保留本文地址:http://www.cnblogs.com/snser/p/5158064.html]
ListView之性能优化的更多相关文章
- 转-ListView的性能优化之convertView和viewHolder
ListView的性能优化之convertView和viewHolder 2014-05-14 参考:http://www.cnblogs.com/xiaowenji/archive/2010/12/ ...
- 【Android】以BaseAdapter做适配器的ListView及其性能优化
适配器的Java类 package com.app.adapter; import org.json.JSONArray; import org.json.JSONObject; import and ...
- 安卓ListView的性能优化
在安卓APP中LIstView这个控件可以说基本上是个APP就会用到,但是关于ListView除了需要了解其最基本的用法外,作为一个要做出高性能APP的程序员还需了解一些关于LIstView控件性能优 ...
- ListView的性能优化之convertView和viewHolder
转载请注明出处 最近碰到的面试题中经常会碰到问"ListView的优化"问题.所以就拿自己之前写的微博客户端的程序做下优化. 自己查了些资料,看了别人写的博客,得出结论,ListV ...
- 安卓中listview中性能优化的处理
1.在adapter中的getView方法中尽量少使用逻辑 不要在你的getView()中写过多的逻辑代码,我们能够将这些代码放在别的地方.比如: 优化前的getView(): @Override p ...
- ListView的性能优化
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHol ...
- Android进阶笔记14:ListView篇之ListView性能优化
1. 首先思考一个问题ListView如何才能提高效率 ? 当convertView为空时候,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象.当convertView不 ...
- ListView性能优化——convertView&viewHolder
ListView优化大致从以下几个角度:1.复用已经生成的convertView:2.添加viewHolder类:3.缓存数据(图片缓存):4.分页加载. 具体方案: 1.如果自定义适配器,那么在ge ...
- Android进阶笔记11:ListView篇之ListView性能优化
1. 首先思考一个问题ListView如何才能提高效率 ? 当convertView为空时候,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象.当convertView不 ...
随机推荐
- YY一下十年后的自己
ps:其实这篇文章的评论比文章本身更有意思,欢迎关注. 每到年底总是我最焦虑的时候,年龄越大情况越明显. 可能越长大越是对 时光的流逝 更有感触,有感触之后就会胡思乱想.所以随手开始写下这篇文章. 人 ...
- 跟我一起hadoop(1)-hadoop2.6安装与使用
伪分布式 hadoop的三种安装方式: Local (Standalone) Mode Pseudo-Distributed Mode Fully-Distributed Mode 安装之前需要 $ ...
- ABP(现代ASP.NET样板开发框架)系列之21、ABP展现层——Javascript函数库
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之21.ABP展现层——Javascript函数库 ABP是“ASP.NET Boilerplate Project ...
- ERROR ITMS-90167: "No .app bundles found in the package"错误
ERROR ITMS-90167: "No .app bundles found in the package" 出现如上错误请查检以下2个方向: 1.macOS Sierra 1 ...
- idea报错:error java compilation failed internal java compiler error
idea下面报如下问题 error java compilation failed internal java compiler error 解决办法:Setting->Compiler-> ...
- SQL Server 监控系列(文章索引)
一.前言(Introduction) SQL Server监控在很多时候可以帮助我们了解数据库做了些什么,比如谁谁在什么时候修改了表结构,谁谁在删除了某个对象,当这些事情发生了,老板在后面追着说这是谁 ...
- iOS 支持 IPv6
苹果的规定:2016年6月1日提交到App Store必须支持IPv6-only网络. 官方文档:https://developer.apple.com/library/mac/documentati ...
- java监控之ManagementFactory分析
The ManagementFactory class is a factory class for getting managed beans for the Java platform. This ...
- js模块定义——支持CMD&AMD&直接加载
/* animate */ //直接加载 (function() { var animate = {} //balabala window.animate = animate; })(); //AMD ...
- JavaScript知识点总结(命名规范,变量的作用域)
命名规范 有人说JavaScript的宽容性是这个语言最糟糕的方面之一.比如说想把2个数字加在一起,JavaScript会把其中一个数字解析成字符串,那么就会得到一个奇怪的字符串,而不是2个数字的和. ...