ListView 编程: 如何优化自定义 Adapter

使用自定义的 Adapter,需要优化,说到底是优化我们自定义的适配器类!

再说到底就是优化回调方法 getView 方法。


ListView 编程: Adapter 何方神圣?博客中,只是简单的介绍了如何去自定义一个适配器以及注意事项。


但是.......

如果像ListView 编程: Adapter 何方神圣?中的示例代码那样去写程序的话,那么估计要被老大BS的,呵呵!


那么,结合 Google IO 的建议、APIDemo 代码以及 个人见解,作进一步的优化工作。


说明:该博客使用的布局文件与ListView 编程: Adapter 何方神圣?附录中一致。


整理代码,为优化做准备


Activity 代码

  1. packagemark.zhang;
  2. importjava.util.ArrayList;
  3. importandroid.app.ListActivity;
  4. importandroid.os.Bundle;
  5. publicclassFileActivityextendsListActivity{
  6. publicvoidonCreate(BundlesavedInstanceState){
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. //模擬添加數據
  10. ArrayList<String>arrays=newArrayList<String>();
  11. for(inti=0;i<30;i++){
  12. arrays.add(""+i);
  13. }
  14. //实例化自定义Adapter
  15. FileViewAdapteradapter=newFileViewAdapter(this,arrays);
  16. //设置适配器
  17. setListAdapter(adapter);
  18. }
  19. }

FileViewAdapter 代码

  1. packagemark.zhang;
  2. importjava.util.ArrayList;
  3. importandroid.content.Context;
  4. importandroid.util.Log;
  5. importandroid.view.LayoutInflater;
  6. importandroid.view.View;
  7. importandroid.view.ViewGroup;
  8. importandroid.widget.BaseAdapter;
  9. importandroid.widget.ImageView;
  10. importandroid.widget.TextView;
  11. publicclassFileViewAdapterextendsBaseAdapter{
  12. privateLayoutInflaterinflater=null;
  13. privateArrayList<String>arrays=null;
  14. publicFileViewAdapter(Contextcontext,ArrayList<String>arrays){
  15. this.arrays=arrays;
  16. inflater=LayoutInflater.from(context);
  17. }
  18. @Override
  19. publicintgetCount(){
  20. Log.d("mark","getCount()isinvoked!");
  21. //返回需要顯示的item數目
  22. //這次是外界提供的數據,與上次代碼有差異
  23. returnarrays.size();
  24. }
  25. @Override
  26. publicObjectgetItem(intposition){
  27. Log.d("mark","getItem()isinvoked!");
  28. returnposition;
  29. }
  30. @Override
  31. publiclonggetItemId(intposition){
  32. Log.d("mark","getItemId()isinvoked!");
  33. Log.d("mark","position="+position);
  34. returnposition;
  35. }
  36. @Override
  37. publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
  38. Log.d("mark","getView()isinvoked!");
  39. Viewv=inflater.inflate(R.layout.custom_fileview,null);
  40. ((ImageView)v.findViewById(R.id.image_pic)).setImageResource(R.drawable.file);
  41. ((TextView)v.findViewById(R.id.text_content)).setText("fileName");
  42. returnv;
  43. }
  44. }

运行效果:


ListView 编程: 如何优化自定义 Adapter

ok,对代码的重新整理算是为下面的优化做准备。


在接下来的优化方案里,主要是针对 FileViewAdapter 的 getView 方法进行优化,其他代码不变。


说优化之前,先搞明白一个问题:getView 方法的三个参数


getView 方法有三个参数,各个参数的含义可以咨询 SDK API 文档。


修改代码(就是修改 Log 输出):

  1. @Override
  2. publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
  3. Log.d("mark","getView()isinvoked!"+"position="+position+","
  4. +"convertView="+convertView+","+"parent="+parent);
  5. Viewv=inflater.inflate(R.layout.custom_fileview,null);
  6. ((ImageView)v.findViewById(R.id.image_pic))
  7. .setImageResource(R.drawable.file);
  8. ((TextView)v.findViewById(R.id.text_content)).setText("fileName");
  9. returnv;
  10. }

运行 APP,打印信息:


ListView 编程: 如何优化自定义 Adapter


可以看出,getView 方法被调用了 16(0-15) 次,不是 30 次。


仔细看看模拟器就可以知道当前显示的 item

是 16(下面还有一个显示不全,应该是 17 个,从下面的打印信息可以看出) 个.

不是 30 个。


那么,我们滑动当前视图中的滚动条,试一试。


ListView 编程: 如何优化自定义 Adapter


可以看出,getView 方法确实是调用了 30 次(0-29)。
你还可以发现,(滚动)显示的打印信息中,convertView 不是 null 了。

我们再次将滚动条滚动到顶部,发现打印信息中 convertView 也不是 null 了。


换句话说:

利用 convertView 这个参数,而不去在 getView 方法中重新创建一个临时的变量 View 了,

那么可以减轻虚拟机(回收)的负担,从而提高效率。


方案 1_ 优化代码: 使用convertView


@Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.d("mark", "getView() is invoked!" + "position = " + position + ","
+ "convertView = " + convertView + "," + "parent = " + parent);
if (convertView == null) {
convertView = inflater.inflate(R.layout.custom_fileview, null);
}


((ImageView) convertView.findViewById(R.id.image_pic))
.setImageResource(R.drawable.file);
((TextView) convertView.findViewById(R.id.text_content))
.setText("fileName");

return convertView;
}


可以看出,只有 convertView == null 为真(上面测试已经说明 convertView 何时为 null),

才去创建 View 对象。

如果你有兴趣的话,可以再次运行 APP 可以看出从底部再次滚动到顶部,

反复几次,你会发现几乎很少创建 View 对象,而是重复利用原来已经存在的 View 对象。

下面还有一种方式来优化代码,不说是最好但至少是 even better (Google 推荐)!


方案 2_ 优化代码:hold 一把


在 android 提供的 APIDemo 中(List14.java)使用了 ViewHolder ,

所以 ViewHolder 不是 android 自带的 api,也不是什么诡异的东西。


@Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.d("mark", "getView() is invoked!" + "position = " + position + ","
+ "convertView = " + convertView + "," + "parent = " + parent);

ViewHolder vHolder = null;

if (convertView == null) {
convertView = inflater.inflate(R.layout.custom_fileview, null);
// 創建 ViewHodler 對象
vHolder = new ViewHolder();
vHolder.pic = (ImageView) convertView.findViewById(R.id.image_pic);
vHolder.content = (TextView) convertView
.findViewById(R.id.text_content);
// 設置 Tag
convertView.setTag(vHolder);
} else {
vHolder = (ViewHolder) convertView.getTag();
}
vHolder.pic.setImageResource(R.drawable.file);
vHolder.content.setText("fileName");
return convertView;
}


其中 ViewHolder 是 FileViewAdapter 的 一个静态内部类。

static class ViewHolder {
TextView content;
ImageView pic;
}


使用 ViewHolder 的关键好处是缓存了显示数据的视图,加快了 UI 的响应速度。


到目前为止,仿佛优化工作已经 ok,其实,

还有一个小问题: ImageView 使用的图片资源需要预处理。


FileViewAdapter 完整的代码如下:

  1. packagemark.zhang;
  2. importjava.util.ArrayList;
  3. importandroid.content.Context;
  4. importandroid.graphics.Bitmap;
  5. importandroid.graphics.BitmapFactory;
  6. importandroid.util.Log;
  7. importandroid.view.LayoutInflater;
  8. importandroid.view.View;
  9. importandroid.view.ViewGroup;
  10. importandroid.widget.BaseAdapter;
  11. importandroid.widget.ImageView;
  12. importandroid.widget.TextView;
  13. publicclassFileViewAdapterextendsBaseAdapter{
  14. privateLayoutInflaterinflater=null;
  15. privateArrayList<String>arrays=null;
  16. privateBitmapshowIcon=null;
  17. publicFileViewAdapter(Contextcontext,ArrayList<String>arrays){
  18. this.arrays=arrays;
  19. inflater=LayoutInflater.from(context);
  20. //處理圖片資源
  21. showIcon=BitmapFactory.decodeResource(context.getResources(),
  22. R.drawable.file);
  23. }
  24. @Override
  25. publicintgetCount(){
  26. //Log.d("mark","getCount()isinvoked!");
  27. //返回需要顯示的item數目
  28. //這次是外界提供的數據,與上次代碼有差異
  29. returnarrays.size();
  30. }
  31. @Override
  32. publicObjectgetItem(intposition){
  33. //Log.d("mark","getItem()isinvoked!");
  34. returnposition;
  35. }
  36. @Override
  37. publiclonggetItemId(intposition){
  38. //Log.d("mark","getItemId()isinvoked!");
  39. //Log.d("mark","position="+position);
  40. returnposition;
  41. }
  42. @Override
  43. publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
  44. Log.d("mark","getView()isinvoked!"+"position="+position+","
  45. +"convertView="+convertView+","+"parent="+parent);
  46. ViewHoldervHolder=null;
  47. if(convertView==null){
  48. convertView=inflater.inflate(R.layout.custom_fileview,null);
  49. //創建ViewHodler對象
  50. vHolder=newViewHolder();
  51. vHolder.pic=(ImageView)convertView.findViewById(R.id.image_pic);
  52. vHolder.content=(TextView)convertView
  53. .findViewById(R.id.text_content);
  54. //設置Tag
  55. convertView.setTag(vHolder);
  56. }else{
  57. vHolder=(ViewHolder)convertView.getTag();
  58. }
  59. //設置位圖
  60. vHolder.pic.setImageBitmap(showIcon);
  61. vHolder.content.setText("fileName");
  62. returnconvertView;
  63. }
  64. staticclassViewHolder{
  65. TextViewcontent;
  66. ImageViewpic;
  67. }
  68. }

最后强调一下,要在布局文件里面将 ListView 控件的属性设置为:


android:layout_width="fill_parent"
android:layout_height="fill_parent"


关于 ListView 的工作原理(Recycler)以及如何进一步优化 ListView,可以参考

http://mzh3344258.blog.51cto.com/1823534/889879


Google IO 文档下载

http://download.****.net/detail/androidbluetooth/3783925

转自:http://blog.****.net/veryitman/article/details/6960936