1,listview加载性能优化ViewHolder

转自: http://blog.csdn.net/jacman/article/details/7087995

在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局,但当listview有大量的数据需要加载的时候,会占据大量内存,影响性能,这时候就需要按需填充并重新使用view来减少对象的创建。

ListView加载数据都是在public View getView(int position, View convertView, ViewGroup parent) {}方法中进行的(要自定义listview都需要重写listadapter:如BaseAdapter,SimpleAdapter,CursorAdapter的等的getvView方法),优化listview的加载速度就要让convertView匹配列表类型,并最大程度上的重新使用convertView。

getview的加载方法一般有以下三种种方式:

最慢的加载方式是每一次都重新定义一个View载入布局,再加载数据

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

View item = mInflater.inflate(R.layout.list_item_icon_text, null);

((TextView) item.findViewById(R.id.text)).setText(DATA[position]);

((ImageView) item.findViewById(R.id.icon)).setImageBitmap(

(position & 1) == 1 ? mIcon1 : mIcon2);

return item;

}

正确的加载方式是当convertView不为空的时候直接重新使用convertView从而减少了很多不必要的View的创建,然后加载数据

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

if (convertView == null) {

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

}

((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]);

((ImageView) convertView.findViewById(R.id.icon)).setImageBitmap(

(position & 1) == 1 ? mIcon1 : mIcon2);

return convertView;

}

最快的方式是定义一个ViewHolder,将convetView的tag设置为ViewHolder,不为空时重新使用即可

static class ViewHolder {

TextView text;

ImageView icon;

}

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

ViewHolder holder;

if (convertView == null) {

convertView = mInflater.inflate(R.layout.list_item_icon_text,

parent, false);

holder = new ViewHolder();

holder.text = (TextView) convertView.findViewById(R.id.text);

holder.icon = (ImageView) convertView.findViewById(R.id.icon);

convertView.setTag(holder);

} else {

holder = (ViewHolder) convertView.getTag();

}

holder.text.setText(DATA[position]);

holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);

return convertView;

}

三种方式加载效率对比如下图所示:

说明:上述三个例子代码摘自google 2010 I/O大会

当处理一些耗时的资源加载的时候需要做到以下几点,以使你的加载更快更平滑:

1.   适配器在界面主线程中进行修改

2.   可以在任何地方获取数据但应该在另外一个地方请求数据

3.   在主界面的线程中提交适配器的变化并调用notifyDataSetChanged()方法

2,利用convertView优化ListView性能

转自: http://blog.csdn.net/cc_lq/article/details/7090142

这里提到的ListView只是作为一个典型代表 其实在Android中 采用类似Adapter机制的GridView等都是可以适用的 而ListView应该是用得最多的 所以就以它来举例

大家都知道 将ListView和Adapter绑定以后 其实也就是将数据源和控件显示绑定在一起 而每次需要显示ListView的时候 里面的item其实是Adapter提供的 通过的就是重要的getView()方法 而做法也是在这上面进行

先来看一下基本的getView写法

  1. public View getView(int position, View convertView, ViewGroup parent) {
  2. View view = new View();
  3. //通过inflate等找到布局 然后findViewById等 设置各个显示的item
  4. return view;
  5. }

而在ListView滑动的过程中 很容易就会发现每次getView被执行 都会new出一个View对象 长此以往会产生很大的消耗 特别当item中还有Bitmap等 甚至会造成OOM的错误导致程序崩溃

在看getView提供的参数时 可能已经注意到了 有一个参数View convertView 而这个convertView其实就是最关键的部分了 原理上讲 当ListView滑动的过程中 会有item被滑出屏幕 而不再被使用 这时候Android会回收这个条目的view 这个view也就是这里的convertView

在上面的做法中 当item1被移除屏幕的时候 我们会重新new一个View给新显示的item_new 而如果使用了这个convertView 我们其实可以复用它 这样就省去了new View的大量开销

下面就是使用convertView后的情况

  1. public View getView(int position, View convertView, ViewGroup parent) {
  2. View view = null;
  3. if (convertView != null) {
  4. view = convertView;
  5. //复用了回收的view 只需要直接作内容填充的修改就好了
  6. } else {
  7. view = new Xxx(...);
  8. //没有供复用的view 按一般的做法新建view
  9. }
  10. return view;
  11. }

这样一来 就避免了反复创建大量view的问题了

但是上面的仍然有缺陷 当我们的ListView中填充的item有多种形式时 比如微博中 有的item中包含图片 有的item包含视频 那么必然的 我们需要用到2种item的布局方式

此时如果只是单纯判断convert是否存在 会造成回收的view不符合你当前需要的布局 而类似转换失败出错退出

这里要提到Adapter中的另外2个方法:

public int getItemViewType(int position) {}

public int getViewTypeCount() {}

从方法名上 就可以比较明显的明白这2个的作用

下面附上一个demo代码

  1. class MyAdapter extends BaseAdapter{
  2. Context mContext;
  3. LinearLayout linearLayout = null;
  4. LayoutInflater inflater;
  5. TextView tex;
  6. final int VIEW_TYPE = 2;
  7. final int TYPE_1 = 0;
  8. final int TYPE_2 = 1;
  9. public MyAdapter(Context context) {
  10. mContext = context;
  11. inflater = LayoutInflater.from(mContext);
  12. }
  13. @Override
  14. public int getCount() {
  15. return listString.size();
  16. }
  17. //每个convert view都会调用此方法,获得当前所需要的view样式
  18. @Override
  19. public int getItemViewType(int position) {
  20. int p = position%6;
  21. if(p == 0)
  22. return TYPE_1;
  23. else if(p < 3)
  24. return TYPE_2;
  25. else
  26. return TYPE_1;
  27. }
  28. @Override
  29. public int getViewTypeCount() {
  30. return 2;
  31. }
  32. @Override
  33. public Object getItem(int arg0) {
  34. return listString.get(arg0);
  35. }
  36. @Override
  37. public long getItemId(int position) {
  38. return position;
  39. }
  40. @Override
  41. public View getView(int position, View convertView, ViewGroup parent) {
  42. viewHolder1 holder1 = null;
  43. viewHolder2 holder2 = null;
  44. int type = getItemViewType(position);
  45. //无convertView,需要new出各个控件
  46. if(convertView == null)
  47. {
  48. //按当前所需的样式,确定new的布局
  49. switch(type)
  50. {
  51. case TYPE_1:
  52. convertView = inflater.inflate(R.layout.listitem1, parent, false);
  53. holder1 = new viewHolder1();
  54. holder1.textView = (TextView)convertView.findViewById(R.id.textview1);
  55. holder1.checkBox = (CheckBox)convertView.findViewById(R.id.checkbox);
  56. convertView.setTag(holder1);
  57. break;
  58. case TYPE_2:
  59. convertView = inflater.inflate(R.layout.listitem2, parent, false);
  60. holder2 = new viewHolder2();
  61. holder2.textView = (TextView)convertView.findViewById(R.id.textview2);
  62. holder2.imageView = (ImageView)convertView.findViewById(R.id.imageview);
  63. convertView.setTag(holder2);
  64. break;
  65. }
  66. }
  67. else
  68. {
  69. //有convertView,按样式,取得不用的布局
  70. switch(type)
  71. {
  72. case TYPE_1:
  73. holder1 = (viewHolder1) convertView.getTag();
  74. break;
  75. case TYPE_2:
  76. holder2 = (viewHolder2) convertView.getTag();
  77. break;
  78. }
  79. //设置资源
  80. switch(type)
  81. {
  82. case TYPE_1:
  83. holder1.textView.setText(Integer.toString(position));
  84. holder1.checkBox.setChecked(true);
  85. break;
  86. case TYPE_2:
  87. holder2.textView.setText(Integer.toString(position));
  88. holder2.imageView.setBackgroundResource(R.drawable.icon);
  89. break;
  90. }
  91. }
  92. return convertView;
  93. }
  94. }
  95. //各个布局的控件资源
  96. class viewHolder1{
  97. CheckBox checkBox;
  98. TextView textView;
  99. }
  100. class viewHolder2{
  101. ImageView imageView;
  102. TextView textView;
  103. }

这里对于每个View使用了一个viewHolder来控制其内部的子item

还有一个需要注意的地方是使用了setTag和getTag的方法 将holder绑定到了view上 也算一种技巧

以上基本就是主要的内容了 下面再补充实际操作当中的一些Tips

*如果convertView上用Type区分有些繁琐 或者不需要那么复杂 只是很少有出现不同的情况 那么还可以在取得convertView后 通过java提供的 instanceof 来判断是否可以强转 如果不能强转 就去新建一个View的做法 但是其实这种做法并不规范 所以还是推荐上面的做法

*第二个是关于ListView 对于纯色的item背景 其实可以直接设置BackgroundColor 而不要使用图片 这一部分其实可以有不小的提升 同样的 对于任何纯色的背景 应该尽量去设置RGB颜色 而不是全用一张图片做背景

转自:http://johncookie.iteye.com/blog/1250049

3,android listview优化几种写法详细介绍

转自: http://www.jb51.net/article/31886.htm

这篇文章只是总结下getView里面优化视图的几种写法,就像孔乙己写茴香豆的茴字的几种写法一样,高手勿喷,勿笑,只是拿出来分享,有错误的地方欢迎大家指正,谢谢。 
listview 
Aviewthatshowsitemsinaverticallyscrollinglist。 
一个显示一个垂直的滚动子项的列表视图在android开发中,使用listview的地方很多,用它来展现数据,成一个垂直的视图。使用listview是一个标准的适配器模式,用数据--,界面--xml以及适配器--adapter,数据被适配器按照需要的方式展现出来,xml描写了数据如何展现,activity中控制这些活动。 
其中使用自定义的adapter,会要重写getView方法,在getView方法产生给用户item的视图以及数据。 
见图: 
 
这里有一个优化的地方,就是重用view,这样减少内存消耗,同时加快item加载速度。 
在getView中优化的地方,大家想必都非常情况,下面我总结了三种优化的写法,请大家指正。 
第一: 
重用了convertView,很大程度上的减少了内存的消耗。通过判断convertView是否为null,是的话就需要产生一个视图出来,然后给这个视图数据,最后将这个视图返回给底层,呈献给用户。 
特点:如果当前的convertView为null,则通过LayoutInflat产生一个view。

复制代码代码如下:
ViewCode 
publicViewgetView(intposition,ViewconvertView,ViewGroupparent) 

if(convertView==null) 

convertView=LayoutInflater.from(context).inflate(R.layout.section_list_item1,null); 

TextViewtv_name=(TextView)convertView.findViewById(R.id.contact_contactinfoitem_tv_name);
TextViewtv_phone=(TextView)convertView.findViewById(R.id.contact_contactinfoitem_tv_phoneNum); 
ContactInfo1confo=contacts.get(position); 
if(confo!=null){//toseteveryitem'stext 
tv_name.setText(confo.getContactName()); 
tv_phone.setText(confo.getContact_Phone()); 

returnconvertView; 

第二: 
上面的写法会有一个缺点,就是每次在getVIew的时候,都需要重新的findViewById,重新找到控件,然后进行控件的赋值以及事件相应设置。这样其实在做重复的事情,因为的geiview中,其实包含有这些控件,而且这些控件的id还都是一样的,也就是其实只要在view中findViewById一次,后面无需要每次都要findViewById了。 
下面给出第二种写法 
写发的特点,通常有一个内部类classViewHolder,这个ViewHolder,用来标识view中一些控件,方便进行一些事件相应操作的设置,比如onClick等等,这样可以不用每次都要findViewById了,减少了性能的消耗。同时重用了convertView,很大程度上的减少了内存的消耗。

复制代码代码如下:
ViewCode 
publicViewgetView(intposition,ViewconvertView,ViewGroupparent) 

ViewHolderholder; 
if(convertView==null){ 
convertView=LayoutInflater.from(context).inflate(R.layout.section_list_item1,null); 
holder=newViewHolder(); 
holder.tv_name=(TextView)convertView.findViewById(R.id.contact_contactinfoitem_tv_name); 
holder.tv_phone=(TextView)convertView.findViewById(R.id.contact_contactinfoitem_tv_phoneNum); 
convertView.setTag(holder); 

else 

holder=(ViewHolder)convertView.getTag(); 

ContactInfo1confo=contacts.get(position); 
Log.i("my","confo"+confo.getContactName()); 
if(confo!=null){//toseteveryitem'stext

holder.tv_name.setText(confo.getContactName()); 
holder.tv_phone.setText(confo.getContact_Phone()); 

returnconvertView; 

classViewHolder 

TextViewtv_name,tv_phone; 

第三: 
 个人觉得这个写法是最舒服的,最舒服的意思是看着代码有一种很爽,看的很清晰。 
特点,使用了内部类classViewHolder、重用了convertView。 
区别第二种写法是,使用了一个临时变量Viewview=convertView,然后修改view,最后返回view

复制代码代码如下:
ViewCode 
@Override 
publicViewgetView(intposition,ViewconvertView,ViewGroupparent) 

Viewview=convertView; 
ViewHolderholder; 
if(view==null){ 
view=LayoutInflater.from(context).inflate(R.layout.section_list_item1,null); 
holder=newViewHolder(); 
holder.tv_name=(TextView)view.findViewById(R.id.contact_contactinfoitem_tv_name); 
holder.tv_phone=(TextView)view.findViewById(R.id.contact_contactinfoitem_tv_phoneNum); 
view.setTag(holder); 

else 

holder=(ViewHolder)view.getTag(); 

ContactInfo1confo=contacts.get(position); 
Log.i("my","confo"+confo.getContactName()); 
if(confo!=null){//toseteveryitem'stext

holder.tv_name.setText(confo.getContactName()); 
holder.tv_phone.setText(confo.getContact_Phone()); 

returnview; 

classViewHolder 

TextViewtv_name,tv_phone; 

以上就是集中写法,供新手学习和总结。 
源代码如下:LisViewTest.zip 
根据楼下朋友提供的建议,发现还有优化的地方,最新更新如下:

复制代码代码如下:
ViewCode 
@Override 
publicViewgetView(intposition,ViewconvertView,ViewGroupparent) 

Viewview=convertView; 
ViewHolderholder; 
if(view==null){ 
view=LayoutInflater.from(context).inflate(R.layout.section_list_item1,null); 
holder=newViewHolder(); 
holder.tv_name=(TextView)view.findViewById(R.id.contact_contactinfoitem_tv_name); 
holder.tv_phone=(TextView)view.findViewById(R.id.contact_contactinfoitem_tv_phoneNum); 
view.setTag(holder); 

else 

holder=(ViewHolder)view.getTag(); 

ContactInfo1confo=contacts.get(position); 
Log.i("my","confo"+confo.getContactName()); 
if(confo!=null){//toseteveryitem'stext

holder.tv_name.setText(confo.getContactName()); 
holder.tv_phone.setText(confo.getContact_Phone()); 

returnview; 

<fontcolor="\"#0000ff\""></font>staticclassViewHolder 

TextViewtv_name,tv_phone; 

注意:staticclassViewHolder 
这里设置ViewHolder为static,也就是静态的,静态类只会在第一次加载时会耗费比较长时间,但是后面就可以很好帮助加载,同时保证了内存中只有一个ViewHolder,节省了内存的开销。 
非常感谢大家提出建议以及大家的关注!

4,[Android]ListView性能优化之视图缓存

http://www.cnblogs.com/over140/archive/2011/03/23/1991100.html

前言

  ListView是Android中最常用的控件,通过适配器来进行数据适配然后显示出来,而其性能是个很值得研究的话题。本文与你一起探讨Google I/O提供的优化Adapter方案,欢迎大家交流。

声明

  欢迎转载,但请保留文章原始出处:)

    博客园:http://www.cnblogs.com

    农民伯伯: http://over140.cnblogs.com

正文

  一、准备

    1.1  了解关于Google IO大会关于Adapter的优化,参考以下文章:

      Android开发之ListView 适配器(Adapter)优化

      Android开发——09Google I/O之让Android UI性能更高效(1)

      PDF下载:Google IO.pdf

    1.2  准备测试代码:

      Activity


    private TestAdapter mAdapter;

    private String[] mArrData;
    private TextView mTV;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mTV = (TextView) findViewById(R.id.tvShow);

        mArrData = new String[1000];
        for (int i = 0; i < 1000; i++) {
            mArrData[i] = "Google IO Adapter" + i;
        }
        mAdapter = new TestAdapter(this, mArrData);
        ((ListView) findViewById(android.R.id.list)).setAdapter(mAdapter);
    }

      代码说明:模拟一千条数据,TestAdapter继承自BaseAdapter,main.xml见文章末尾下载。

  二、测试

    测试方法:手动滑动ListView至position至50然后往回滑动,充分利用convertView不等于null的代码段。

    2.1  方案一

      按照Google I/O介绍的第二种方案,把item子元素分别改为4个和10个,这样效果更佳明显。

      2.1.1  测试代码


        private int count = 0;
        private long sum = 0L;
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            //开始计时
            long startTime = System.nanoTime();
            
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.list_item_icon_text,
                        null);
            }
            ((ImageView) convertView.findViewById(R.id.icon1)).setImageResource(R.drawable.icon);
            ((TextView) convertView.findViewById(R.id.text1)).setText(mData[position]);
            ((ImageView) convertView.findViewById(R.id.icon2)).setImageResource(R.drawable.icon);
            ((TextView) convertView.findViewById(R.id.text2)).setText(mData[position]);
            
            //停止计时
            long endTime = System.nanoTime();
            //计算耗时
            long val = (endTime - startTime) / 1000L;
            Log.e("Test", "Position:" + position + ":" + val);
            if (count < 100) {
                if (val < 1000L) {
                    sum += val;
                    count++;
                }
            } else
                mTV.setText(String.valueOf(sum / 100L));//显示统计结果
            return convertView;
        }

      2.1.2  测试结果(微秒除以1000,见代码)

次数

4个子元素

10个子元素

第一次

366

723

第二次

356

689

第三次

371

692

第四次

356

696

第五次

371

662

    2.2  方案二

      按照Google I/O介绍的第三种方案,是把item子元素分别改为4个和10个。

      2.2.1  测试代码


        private int count = 0;
        private long sum = 0L;

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // 开始计时
            long startTime = System.nanoTime();

            ViewHolder holder;
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.list_item_icon_text,
                        null);
                holder = new ViewHolder();
                holder.icon1 = (ImageView) convertView.findViewById(R.id.icon1);
                holder.text1 = (TextView) convertView.findViewById(R.id.text1);
                holder.icon2 = (ImageView) convertView.findViewById(R.id.icon2);
                holder.text2 = (TextView) convertView.findViewById(R.id.text2);
                convertView.setTag(holder);
            }
            else{
                holder = (ViewHolder)convertView.getTag();
            }
            holder.icon1.setImageResource(R.drawable.icon);
            holder.text1.setText(mData[position]);
            holder.icon2 .setImageResource(R.drawable.icon);
            holder.text2.setText(mData[position]);

            // 停止计时
            long endTime = System.nanoTime();
            // 计算耗时
            long val = (endTime - startTime) / 1000L;
            Log.e("Test", "Position:" + position + ":" + val);
            if (count < 100) {
                if (val < 1000L) {
                    sum += val;
                    count++;
                }
            } else
                mTV.setText(String.valueOf(sum / 100L));// 显示统计结果
            return convertView;
        }
    }

    static class ViewHolder {
        TextView text1;
        ImageView icon1;
        TextView text2;
        ImageView icon2;
    }

      2.2.2  测试结果(微秒除以1000,见代码)

次数

4个子元素

10个子元素

第一次

311

417

第二次

291

441

第三次

302

462

第四次

286

444

第五次

299

436

    2.3  方案三

      此方案为“Henry Hu”提示,API Level 4以上提供,这里顺带测试了一下不使用静态内部类情况下性能。

      2.3.1  测试代码

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // 开始计时
            long startTime = System.nanoTime();

            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.list_item_icon_text, null);
                convertView.setTag(R.id.icon1, convertView.findViewById(R.id.icon1));
                convertView.setTag(R.id.text1, convertView.findViewById(R.id.text1));
                convertView.setTag(R.id.icon2, convertView.findViewById(R.id.icon2));
                convertView.setTag(R.id.text2, convertView.findViewById(R.id.text2));
            }
            ((ImageView) convertView.getTag(R.id.icon1)).setImageResource(R.drawable.icon);
            ((ImageView) convertView.getTag(R.id.icon2)).setImageResource(R.drawable.icon);
            ((TextView) convertView.getTag(R.id.text1)).setText(mData[position]);
            ((TextView) convertView.getTag(R.id.text2)).setText(mData[position]);

            // 停止计时
            long endTime = System.nanoTime();
            // 计算耗时
            long val = (endTime - startTime) / 1000L;
            Log.e("Test", "Position:" + position + ":" + val);
            if (count < 100) {
                if (val < 1000L) {
                    sum += val;
                    count++;
                }
            } else
                mTV.setText(String.valueOf(sum / 100L) + ":" + nullcount);// 显示统计结果
            return convertView;
        }

      2.3.2  测试结果(微秒除以1000,见代码)

        第一次:450

        第二次:467

        第三次:472

        第四次:451

        第五次:441
  四、总结

    4.1  首先有一个认识是错误的,我们先来看截图:

      

      

      可以发现,只有第一屏(可视范围)调用getView所消耗的时间远远多于后面的,通过对

convertView == null内代码监控也是同样的结果。也就是说ListView仅仅缓存了可视范围内的View,随后的滚动都是对这些View进行数据更新。不管你有多少数据,他都只用ArrayList缓存可视范围内的View,这样保证了性能,也造成了我以为ListView只缓存View结构不缓存数据的假相(不会只有我一人这么认为吧- - #)。这也能解释为什么GOOGLE优化方案一比二高很多的原因。那么剩下的也就只有findViewById比较耗时了。据此大家可以看看AbsListView的源代码,看看

obtainView这个方法内的代码及RecycleBin这个类的实现,欢迎分享。

      此外了解这个原理了,那么以下代码不运行你可能猜到结果了:


            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.list_item_icon_text, null);
                ((ImageView) convertView.findViewById(R.id.icon1)).setImageResource(R.drawable.icon);
                ((TextView) convertView.findViewById(R.id.text1)).setText(mData[position]);
                ((ImageView) convertView.findViewById(R.id.icon2)).setImageResource(R.drawable.icon);
                ((TextView) convertView.findViewById(R.id.text2)).setText(mData[position]);
            }
            else
                return convertView;

      没错,你会发现滚动时会重复显示第一屏的数据!

      子控件里的事件因为是同一个控件,也可以直接放到convertView == null 代码块内部,如果需要交互数据比如position,可以通过tag方式来设置并获取当前数据。

    4.2  本文方案一与方案二对比

      这里推荐如果只是一般的应用(一般指子控件不多),无需都是用静态内部类来优化,使用第二种方案即可;反之,对性能要求较高时可采用。此外需要提醒的是这里也是用空间换时间的做法,View本身因为setTag而会占用更多的内存,还会增加代码量;而findViewById会临时消耗更多的内存,所以不可盲目使用,依实际情况而定。

    4.3  方案三

      此方案为“Henry Hu”提示,API Level 4以上支持,原理和方案三一致,减少findViewById次数,但是从测试结果来看效果并不理想,这里不再做进一步的测试。

  五、推荐文章

    Android,谁动了我的内存(1)

    Android 内存泄漏调试

  六、后期维护

2011-3-30  参见这里(http://www.javaeye.com/topic/971782)的讨论,据此将计划写续篇。

结束

  对于Google I/O大会这个优化方案一直抱迟疑态度,此番测试总算是有了更进一步的了解,欢迎大家先测试后交流,看看还有什么办法能够再优化一点。

5,Android开发——09Google I/O之让Android UI性能更高效(1)

http://www.cnblogs.com/halzhang/archive/2010/12/05/1896791.html

一、前言

前几天发现09年Google IO大会关于移动应用方面的主题有一些不错的PPT,对移动应用开发很有帮助。自己看了一些,边看边和大家分享。既然是PPT就很简化了,我会根据实际情况写一些Demo供大家参考。

Android在UI优化方面可以从以下五个方面入手:

  • Adapter优化
  • 背景和图片优化
  • 绘图优化
  • 视图和布局优化
  • 内存分配优化

    二、Adapter优化

    什么是Adapter,可以先看看我的上一篇文章,Android开发——说说Adapter那点事 Adapter与View的连接主要依靠getView这个方法返回我们需要的自定义view。ListView是Android app中一个最最最常用的控件了,所以如何让ListView流畅运行,获取良好的用户体验是非常重要的。对ListView优化就是对Adapter中的getView方法进行优化。09年的Google IO大会给出的优化建议如下:

    Adapter优化示例代码:

       1: @Override
       2: public View getView(int position, View convertView, ViewGroup parent) {
       3:     Log.d("MyAdapter", "Position:" + position + "---"
       4:             + String.valueOf(System.currentTimeMillis()));
       5:     ViewHolder holder;
       6:     if (convertView == null) {
       7:         final LayoutInflater inflater = (LayoutInflater) mContext
       8:                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
       9:         convertView = inflater.inflate(R.layout.list_item_icon_text, null);
      10:         holder = new ViewHolder();
      11:         holder.icon = (ImageView) convertView.findViewById(R.id.icon);
      12:         holder.text = (TextView) convertView.findViewById(R.id.text);
      13:         convertView.setTag(holder);
      14:     } else {
      15:         holder = (ViewHolder) convertView.getTag();
      16:     }
      17:     holder.icon.setImageResource(R.drawable.icon);
      18:     holder.text.setText(mData[position]);
      19:     return convertView;
      20: }
      21:  
      22: static class ViewHolder {
      23:     ImageView icon;
      24:  
      25:     TextView text;
      26: }
    以上是Google io大会上给出的优化建议,经过尝试ListView确实流畅了许多。
     
       1: @Override
       2:  public View getView(int position, View convertView, ViewGroup parent) {
       3:      Log.d("MyAdapter", "Position:" + position + "---"
       4:              + String.valueOf(System.currentTimeMillis()));
       5:          final LayoutInflater inflater = (LayoutInflater) mContext
       6:                  .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
       7:          View v = inflater.inflate(R.layout.list_item_icon_text, null);
       8:          ((ImageView) v.findViewById(R.id.icon)).setImageResource(R.drawable.icon);
       9:          ((TextView) v.findViewById(R.id.text)).setText(mData[position]);
      10:         return v;
      11:  }
    以上是不建议的做法!!
     
    不过我们还是要怀疑一下,SO,我们还是来测试对比一下。
     
    测试说明:
    大家可以看到在getView的时候我们通过log打印出position和当前系统时间。我们通过初始化1000条数据到Adapter显示到ListView,然后滚动到底部,计算出position=0和position=999时的时间间隔。
    测试机子:HTC Magic
    测试实录:打开测序,让ListView一直滚动底部。:-)
  • 测试结果:

    两种情况在操作过程中体验明显不同,在优化的情况下流畅很多很多!

    1、优化建议测试结果:

    12-05 10:44:46.039: DEBUG/MyAdapter(13929): Position:0---1291517086043 
    12-05 10:44:46.069: DEBUG/MyAdapter(13929): Position:1---1291517086072 
    12-05 10:44:46.079: DEBUG/MyAdapter(13929): Position:2---1291517086085

    ……

    12-05 10:45:04.109: DEBUG/MyAdapter(13929): Position:997---1291517104112 
    12-05 10:45:04.129: DEBUG/MyAdapter(13929): Position:998---1291517104135 
    12-05 10:45:04.149: DEBUG/MyAdapter(13929): Position:999---1291517104154

    耗时:17967

    2、没优化的测试结果

    12-05 10:51:42.569: DEBUG/MyAdapter(14131): Position:0---1291517502573 
    12-05 10:51:42.589: DEBUG/MyAdapter(14131): Position:1---1291517502590 
    12-05 10:51:42.609: DEBUG/MyAdapter(14131): Position:2---1291517502617

    ……

    12-05 10:52:07.079: DEBUG/MyAdapter(14131): Position:998---1291517527082 
    12-05 10:52:07.099: DEBUG/MyAdapter(14131): Position:999---1291517527108

    耗时:24535

    在1000条记录的情况下就有如此差距,一旦数据nW+,ListView的Item布局更加复杂的时候,优化的作用就更加突出了!

    OK,欢迎大家交流学习,Gtalk:ghanguo@gmail.com

    转载请注明出处!

    示例代码: GoogleIO示例代码

---------EOF------------------

listview优化 汇总的更多相关文章

  1. android webview开发问题及优化汇总

    我们在native与网页相结合开发的过程中,难免会遇到关于WebView一些共通的问题.就我目前开发过程中遇到的问题以及最后得到的优化方案都将在这里列举出来.有些是老生常谈,有些则是个人摸索得出解决方 ...

  2. Android 常驻与很驻型广播的差别,及ListView优化,Android新手基本知识巩固

    1.常驻型广播  常驻型广播,当你的应用程序关闭了,假设有广播信息来,你写的广播接收器相同的能接受到.  他的注冊方式就是在你的应用程序中的AndroidManifast.xml进行注冊. 通常说这样 ...

  3. 内存泄露--contentView缓存使用与ListView优化

    引起Android内存泄露有很多种原因,下面罗列了一些问题,以后会一一解决 1.构造Adapter时没有使用缓存convertView(衍生出ListView优化问题) 2.查询数据库游标没有关闭 3 ...

  4. MySQL优化汇总

    1)mysql优化汇总,转载自网络

  5. Android——ListView优化

    1.ListView基本概念 列表显示需要三个元素: ListView:用来展示列表的View. 适配器:用来把数据映射到ListView上 数据:具体的将被映射的字符串,图片或基本组件 适配器类型分 ...

  6. Android零基础入门第43节:ListView优化和列表首尾使用

    原文:Android零基础入门第43节:ListView优化和列表首尾使用 前面连续几期都在学习ListView的各种使用方法,如果细心的同学可能会发现其运行效率是有待提高的,那么本期就来一起学习有哪 ...

  7. Adaptert Listview 优化

    这次是关于Listview的优化的,之前一直采用愚蠢的方式来使用listview,出现的情况就是数据多的话下拉的时候会出现卡顿的情况,内存占用多.所以学习了关于listview的优化,并且这也是普遍使 ...

  8. Android开发之ListView详解 以及简单的listView优化

    ListView列表视图 最常用的控件之一,使用场景例如:微信,手机QQ等等. android:divider:每个item之间的分割线,可以使用图片或者色值. android:dividerHeig ...

  9. listview优化技术

    1.在adapter中的getView方法中尽量少使用逻辑 2.尽最大可能避免GC 3.滑动的时候不加载图片 4.将ListView的scrollingCache和animateCache设置为fal ...

随机推荐

  1. Android SharedPreferences使用以及原理详解

    SharedPreferences的使用非常简单,能够轻松的存放数据和读取数据.SharedPreferences只能保存简单类型的数据,例如,String.int等.一般会将复杂类型的数据转换成Ba ...

  2. 记录bigdesk中ElasticSearch的性能参数

    定时采集bigdesk中的Elasticsearch性能参数,并保存到数据库或ELK,以便于进行长期监控. 基于python脚本实现,脚本如下: #coding=gbk import httplibi ...

  3. Go中的指针与函数接收器

    Go中使用*号表示指针,但是没有指针算数,不能对其进行加减.同时内存管理都由Go来负责,不需要拖动释放内存. Go中的函数接收者,可以为值类型,也可以是引用类型. 看代码: package main ...

  4. iOS下日期的处理

    NSDate存储的是世界标准时(UTC),输出时需要根据时区转换为本地时间 Dates         NSDate类提供了创建date,比较date以及计算两个date之间间隔的功能.Date对象是 ...

  5. maven学习手记 - 2

    学习目标 maven 的插件和目标: maven 的生命周期和阶段.   前言 在手记1中看到执行 mvn clean package 时,maven 自动执行了compile 和 test 操作. ...

  6. sharepoint 2010 找不到搜索不到ad里的用户

    前提条件: 1.这个用户是在ad中存在的. 2.这个用户也同步到了userprofile中. 问题现象: 在sharepoint的人员选择器中,搜索不到已经添加的用户. 可能原因: 1.有人说需要将 ...

  7. 如何安装altium designer 10

    http://jingyan.baidu.com/article/4dc4084881e2bdc8d946f1f3.html

  8. java中字符串String 转 int(转)

    java中字符串String 转 int String -> int s="12345"; int i; 第一种方法:i=Integer.parseInt(s); 第二种方法 ...

  9. 如何查看系统内置的shell命令及帮助信息

    1.打开终端,输入help命令可以查看系统默认的shell(通常是bash)的内置的shell命令列表: [tansheng@localhost ~]$ help GNU bash, version ...

  10. 如何在Report Builder中使用fnd_profile.value

    在EBS的Report开发中,需要根据客户化的一个Profile来控制用户可以访问的数据,可是在开发的过程中发现一直取不到该Profile的值,后来百度才找到了原因. 解决方法: 1.添加用户参数p_ ...