listview加载的核心是其adapter,本文针对listview加载的性能优化就是对adpter的优化,总共分四个层次:

0、最原始的加载

1、利用convertView

2、利用ViewHolder

3、实现局部刷新

[转载请保留本文地址:http://www.cnblogs.com/goagent/p/5158064.html] 

〇、最原始的加载

这里是不经任何优化的adapter,为了看起来方便,把listview的数据直接在构造函数里传给adapter了,代码如下:

 
 1     private class AdapterOptmL0 extends BaseAdapter {
2 private LayoutInflater mLayoutInflater;
3 private ArrayList<Integer> mListData;
4
5 public AdapterOptmL0(Context context, ArrayList<Integer> data) {
6 mLayoutInflater = LayoutInflater.from(context);
7 mListData = data;
8 }
9
10 @Override
11 public int getCount() {
12 return mListData == null ? 0 : mListData.size();
13 }
14
15 @Override
16 public Object getItem(int position) {
17 return mListData == null ? 0 : mListData.get(position);
18 }
19
20 @Override
21 public long getItemId(int position) {
22 return position;
23 }
24
25 @Override
26 public View getView(int position, View convertView, ViewGroup parent) {
27 View viewRoot = mLayoutInflater.inflate(R.layout.listitem, parent, false);
28 if (viewRoot != null) {
29 TextView txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
30 txt.setText(getItem(position) + "");
31 }
32 return viewRoot;
33 }
34 }

[转载请保留本文地址:http://www.cnblogs.com/goagent/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。

经过优化后的代码如下:

 1     @Override
2 public View getView(int position, View convertView, ViewGroup parent) {
3 if (convertView == null) {
4 convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
5 }
6 if (convertView != null) {
7 TextView txt = (TextView)convertView.findViewById(R.id.listitem_txt);
8 txt.setVisibility(View.VISIBLE);
9 txt.setText(getItem(position) + "");
10 }
11 return convertView;
12 }
 

上述代码加了判断,如果传入的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/goagent/p/5158064.html] 

二、利用ViewHolder

从AdapterOptmL0第27行的警告中,我们还可以看到编译器推荐了一种模型叫ViewHolder,这是个什么东西呢,先看代码:

 
 1     private class AdapterOptmL2 extends BaseAdapter {
2 private LayoutInflater mLayoutInflater;
3 private ArrayList<Integer> mListData;
4
5 public AdapterOptmL2(Context context, ArrayList<Integer> data) {
6 mLayoutInflater = LayoutInflater.from(context);
7 mListData = data;
8 }
9
10 private class ViewHolder {
11 public ViewHolder(View viewRoot) {
12 txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
13 }
14 public TextView txt;
15 }
16
17 @Override
18 public int getCount() {
19 return mListData == null ? 0 : mListData.size();
20 }
21
22 @Override
23 public Object getItem(int position) {
24 return mListData == null ? 0 : mListData.get(position);
25 }
26
27 @Override
28 public long getItemId(int position) {
29 return position;
30 }
31
32 @Override
33 public View getView(int position, View convertView, ViewGroup parent) {
34 if (convertView == null) {
35 convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
36 ViewHolder holder = new ViewHolder(convertView);
37 convertView.setTag(holder);
38 }
39 if (convertView != null && convertView.getTag() instanceof ViewHolder) {
40 ViewHolder holder = (ViewHolder)convertView.getTag();
41 holder.txt.setVisibility(View.VISIBLE);
42 holder.txt.setText(getItem(position) + "");
43 }
44 return convertView;
45 }
46 }
 

从代码中可以看到,这一步做的优化是用一个类ViewHolder来保存listitem里面所有找到的子控件,这样就不用每次都通过耗时的findViewById操作了。

这一步的优化,在listitem布局越复杂的时候效果越为明显。

[转载请保留本文地址:http://www.cnblogs.com/goagent/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/goagent/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) {
}
}

  1     private class AdapterOptmL3Plus extends BaseAdapter implements OnScrollListener{
2 private LayoutInflater mLayoutInflater;
3 private ListView mListView;
4 private ArrayList<Integer> mListData;
5
6 private int mScrollState = SCROLL_STATE_IDLE;
7 private List<Runnable> mPendingNotify = new ArrayList<Runnable>();
8
9 public AdapterOptmL3Plus(Context context, ListView listview, ArrayList<Integer> data) {
10 mLayoutInflater = LayoutInflater.from(context);
11 mListView = listview;
12 mListData = data;
13 mListView.setOnScrollListener(this);
14 }
15
16 private class ViewHolder {
17 public ViewHolder(View viewRoot) {
18 txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
19 }
20 public TextView txt;
21 }
22
23 @Override
24 public int getCount() {
25 return mListData == null ? 0 : mListData.size();
26 }
27
28 @Override
29 public Object getItem(int position) {
30 return mListData == null ? 0 : mListData.get(position);
31 }
32
33 @Override
34 public long getItemId(int position) {
35 return position;
36 }
37
38 @Override
39 public View getView(int position, View convertView, ViewGroup parent) {
40 if (convertView == null) {
41 convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
42 ViewHolder holder = new ViewHolder(convertView);
43 convertView.setTag(holder);
44 }
45 if (convertView != null && convertView.getTag() instanceof ViewHolder) {
46 updateView((ViewHolder)convertView.getTag(), (Integer)getItem(position));
47 }
48 return convertView;
49 }
50
51 public void updateView(ViewHolder holder, Integer data) {
52 if (holder != null && data != null) {
53 holder.txt.setVisibility(View.VISIBLE);
54 holder.txt.setText(data + "");
55 }
56 }
57
58 public void notifyDataSetChanged(final int position) {
59 final Runnable runnable = new Runnable() {
60 @Override
61 public void run() {
62 final int firstVisiablePosition = mListView.getFirstVisiblePosition();
63 final int lastVisiablePosition = mListView.getLastVisiblePosition();
64 final int relativePosition = position - firstVisiablePosition;
65 if (position >= firstVisiablePosition && position <= lastVisiablePosition) {
66 if (mScrollState == SCROLL_STATE_IDLE) {
67 //当前不在滚动,立刻刷新
68 Log.d("Snser", "notifyDataSetChanged position=" + position + " update now");
69 updateView((ViewHolder)mListView.getChildAt(relativePosition).getTag(), (Integer)getItem(position));
70 } else {
71 synchronized (mPendingNotify) {
72 //当前正在滚动,等滚动停止再刷新
73 Log.d("Snser", "notifyDataSetChanged position=" + position + " update pending");
74 mPendingNotify.add(this);
75 }
76 }
77 } else {
78 //不在可视范围内的listitem不需要手动刷新,等其可见时会通过getView自动刷新
79 Log.d("Snser", "notifyDataSetChanged position=" + position + " update skip");
80 }
81 }
82 };
83 runnable.run();
84 }
85
86 @Override
87 public void onScrollStateChanged(AbsListView view, int scrollState) {
88 mScrollState = scrollState;
89 if (mScrollState == SCROLL_STATE_IDLE) {
90 //滚动已停止,把需要刷新的listitem都刷新一下
91 synchronized (mPendingNotify) {
92 final Iterator<Runnable> iter = mPendingNotify.iterator();
93 while (iter.hasNext()) {
94 iter.next().run();
95 iter.remove();
96 }
97 }
98 }
99 }
100
101 @Override
102 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
103 }
104 }

[转载请保留本文地址:http://www.cnblogs.com/goagent/p/5158064.html] 

一步一步实现listview加载的性能优化的更多相关文章

  1. 详解ListView加载网络图片的优化,让你轻松掌握!

    详解ListView加载网络图片的优化,让你轻松掌握! 写博客辛苦了,转载的朋友请标明出处哦,finddreams(http://blog.csdn.net/finddreams/article/de ...

  2. 详解ListView加载网络图片的优化

    我们来了解一些ListView在加载大量网络图片的时候存在的常见问题: 1.性能问题,ListView的滑动有卡顿,不流畅,造成非常糟糕的用户体验. 2.图片的错位问题. 3.图片太大,加载Bitma ...

  3. javascript的页面加载及性能优化(兼容IE7)

    通常来说,window.onload就够用了,如果想加载多个事件,我们可以采取以下方式: window.onload = function(){        func1();        func ...

  4. 前端开发,页面加载速度性能优化,如何提高web页面加载速度

    一个网页访问速度的快慢,  不仅看它服务器的配置,这里除去你空间主机配置很烂的情况以外,我们从网站开发方面来探讨,前端技术需要从哪些方面提高访问的速度,需要用到哪些技术手段. 文件的加载 图标的加载: ...

  5. js动态加载js文件(js异步加载之性能优化篇)

    1.[基本优化] 将所有需要的<script>标签都放在</body>之前,确保脚本执行之前完成页面渲染而不会造成页面堵塞问题,这个大家都懂. 2.[合并JS代码,尽可能少的使 ...

  6. ListView加载性能优化---ViewHolder---分页

    ListView是Android中一个重要的组件,可以使用它加列表数据,用户可以自己定义列表数据,同时ListView的数据加载要借助Adapter,一般情况下要在Adapter类中重写getCoun ...

  7. ListView 加载更多列表 Load More mono forandroid 项目笔记

    今天项目经理找我说Listview加载更多的时候会出现一些问题,主要表现在会顿一下.让我我就去找Java的方法看看.自己写出了mono 的加载更多功能.和大家分享一下 先看效果 首先是模型类ListI ...

  8. [转]listview加载性能优化ViewHolder

    当listview有大量的数据需要加载的时候,会占据大量内存,影响性能,这时候就需要按需填充并重新使用view来减少对象的创建. ListView加载数据都是在public View getView( ...

  9. android之 listview加载性能优化ViewHolder

    在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局,但当listview有大量的数据需要加载的时候, ...

随机推荐

  1. zabbix 监控 oracle 数据库

    https://blog.csdn.net/dyllove98/article/details/41120853

  2. eclipse 创建 maven 项目时如何修改 web 的版本和 jdk 的版本

    eclipse 创建 maven 项目时如何修改 web 的版本和 jdk 的版本 在使用 eclipse 创建 maven 项目的时候,默认的 web.xml 的版本时 2.3,默认 jre 的版本 ...

  3. 宝塔面板php扩展安装

    yum install libmcrypt libmcrypt-devel mcrypt mhash wget http://pecl.php.net/get/mcrypt-1.0.1.tgz tar ...

  4. python爬虫实战(五)--------智联招聘网

    前些天帮同事爬取一些智联招聘网上的关于数据分析的职位信息,他说要做一些数据分析看看,现在已经帮他爬完了.我本来想用Scrapy来爬的,但是不知道为什么爬取的数据和真实搜到的数据不太一样,比如:搜索到的 ...

  5. 【最短路径】 SPFA算法优化

    首先先明确一个问题,SPFA是什么?(不会看什么看,一边学去,传送门),SPFA是bellman-ford的队列优化版本,只有在国内才流行SPFA这个名字,大多数人就只知道SPFA就是一个顶尖的高效算 ...

  6. 应用服务攻击工具clusterd

    应用服务攻击工具clusterd   clusterd是一款Python语言编写的开源应用服务攻击工具.该工具支持七种不同的应用服务平台,如JBoss.ColdFusion.WebLogic.Tomc ...

  7. django使用mongodb建表

    1.安装mongodb的py模块包 pip install mongoengine 同时安装了mongoengine.pymongo 2.在项目配置文件settings.py中配置 from mong ...

  8. NOIP 初赛笔记

    // zj蒟蒻瑟瑟发抖.. // 停课了.要好好努力!——10月8日8:29于机房 1. 1946 年 美国 -> 第一台计算机 2. 真空电子管 -> 晶体管 -> 集成 -> ...

  9. VS2015快捷键及常用功能

    写下这些快捷键的操作,并不是全部记住,记住常用的,然后其他的来查询就好了. 1.回到上一个光标位置/前进到下一个光标位置 1)回到上一个光标位置:使用组合键“Ctrl + -”; 2)前进到下一个光标 ...

  10. 矩阵乘法<简单总结>

    原理:矩阵相乘最重要的方法是一般矩阵乘积.它只有在第一个矩阵的 行数 和第二个矩阵的 列数 相同时才可进行.若A为m×n矩阵,B为n×p矩阵,则他们的乘积AB会是一个m×p矩阵. 若A=    a   ...