Android ListView列表控件的介绍和性能优化

 更新时间:2017年06月09日 08:48:10   作者:tzhenxiong  
这篇文章主要介绍了Android ListView列表控件的介绍和性能优化,需要的朋友可以参考下

ListView列表控件

一、ListView显示数据的原理:mvc模式

m:mode 数据(用javabean规范封装)

v:view ListView

c:adapter 适配器,负责把数据展示到ListView上

二、ListView最常用适配器

BaseAdapter、SimpleAdapter、ArrayAdapter

三、ListView显示数据的步骤

1.创建ListView

2.自定义ListView的适配器继承BaseAdapter,重写baseAdapter的getCount方法和getView方法

3.创建自定义ListView的适配器

4.ListView设置适配器:listView.setAdapter(adapter);

private class ListViewAdapter extends BaseAdapter{
    //返回需要展示的数据的条数
    @Override
    public int getCount() {
      return 200;
    }
    //返回指定position位置对应的数据对象,一般很少用
    @Override
    public Object getItem(int position) {
      return null;
    }
    //返回position位置对应id
    @Override
    public long getItemId(int position) {
      return 0;
    }
    /**
     * 获取一个view,会作为listview的第position个item条目出现,用来显示listview的数据
     * 
     * @param convertView 历史缓存对象
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      View view ;
      if (convertView == null) {
        //创建新的View对象 
        view = View.inflate(MainActivity.this, R.layout.listview_item,null);
      }else{
        //复用历史缓存View对象
        view = convertView;
      }
      TextView tv=(TextView)view.findViewById(R.id.tv);
      tv.setText("item:" + position);
      return view;
    }      
  }

四、ListView的性能优化

1.ListView的奇怪现象

问题:

如果ListView的高度设为包裹内容,getView方法会多次调用

原因:

如果height设为”wrap_content”,为了把所有条目都显示出来,会多次校验数据是否可在屏幕显示完,(校验1次不保准,再多次校验)将多次调用getView方法

解决:以后再使用ListView的时候,高度设置为填充父窗体 android:layout_height=”match_parent”

2.避免内存溢出优化:ListView复用历史缓存View对象

问题:如果不复用历史缓存View对象,当ListView的条目数过多,向下滑的很深时,会报错。

报错信息: E/dalvikvm-heap(2636): Out of memory on a -294967280-byte allocation. 内存溢出

原因:不停创建新的View,消耗内存。为每一个Item都创建一个View对象,必将占用很多内存空间。从xml中生成View,这是属于IO操作,是耗时操作,所以必将影响性能

解决:可以对消失在屏幕的View进行缓存,当往下拖动时复用历史缓存,这样就只创建屏幕能显示的View个数的数量的View。getView方法有个参数convertView就是可以复用的历史缓存View对象。这个对象也可能为空,当它为空的时候,表示该条目view第一次创建,所以我们需要inflate一个view出来。

原理:Android提供了一个叫做Recycler(反复循环)的构件,就是当ListView的Item从滚出屏幕视角之外,对应Item的View会被缓存到Recycler中,相应的会从生成一个Item,而此时调用的getView中的convertView参数就是滚出屏幕的缓存Item的View,所以说如果能重用这个convertView,就会大大改善性能。

public View getView(int position, View convertView, ViewGroup parent) { 
TextView tv; 
if (convertView == null) { 
// 创建新的view 对象 
tv = new TextView(MainActivity.this); 
System.out.println(“创建新的view对象—” + position); 
} else { 
System.out.println(“复用历史缓存对象—” + position); 
view = (TextView) convertView; 
} 
tv.setText(“4月14日科比告别战倒计时:” + position); 
return tv; 
}

3.item子控件显示卡顿优化:ViewHolder重用机制

问题:item中的子控件很多时加载慢。findViewById是到xml文件中去查找对应的id,可以想象如果组件多的话也是挺费事的,如果我们可以让view内的组件也随着view的复用而复用,那该是多美好的一件事。

原因:在getView()方法中的操作是先从xml中创建view对象(inflate操作,我们采用了重用convertView方法优化),然后在这个view去findViewById,找到每一个item的子View的控件对象,如:ImageView、TextView等。这里的findViewById操作是一个树查找过程,也是一个耗时的操作。

解决:谷歌推荐了一种优化方法来做应对,那就是重新建一个内部静态类,里面的成员变量跟view中所包含的组件个数类型相同

private static class ViewHolder {}

基本思路就是在convertView为null的时候,我们不仅重新inflate出来一个view,并且还需要进行findviewbyId的查找工作,但是同时我们还需要获取一个ViewHolder类的对象,并将findviewById的结果赋值给ViewHolder中对应的成员变量。最后将holder对象与该view对象“绑”在一块。当convertView不为null时,我们让view=converView,同时取出这个view对应的holder对象,就获得了这个view对象中的子控件,就是holder中的成员变量,这样在复用的时候,我们就不需要再去findViewById了,只需要在最开始的时候进行数次查找工作就可以了。这里的关键在于如何将view与holder对象进行绑定,那么就需要用到两个方法:View中的setTag和getTag方法了。

经过上面的做法,可能大家感觉不太到优化的效果,根据Google的文档,实际优化效果在百分之5左右。

4.网络数据过多加载缓慢优化

问题:

ListView如果显示本地的List集合中的内容,List的长度也只有100个,我们可以毫不费力一次性加载完这100个数据;但是实际应用中,我们往往会需要使用Listview来显示网络上的内容,比如说我们拿使用ListView显示新闻为例:假如网络情况很好,我们使用的手机也许能够一下子加载完所有新闻数据,然后显示在ListView中,用户可能感觉还好,假如说在网络不太顺畅的情况下,用户加载完所有网络的数据,可能这个list是1000条新闻,那么用户可能需要面对一个空白的Activity好几分钟,这个显然是不合适的。

解决:

我们需要进行分批加载,比如说1000条新闻的List集合,我们一次加载20条,等到用户翻页到底部的时候,我们再添加下面的20条到List中,再使用Adapter刷新ListView,这样用户一次只需要等待20条数据的传输时间,不需要一次等待好几分钟把数据都加载完再在ListView上显示。其次这样也可以缓解很多条新闻一次加载进行产生OOM应用崩溃的情况。

5.加载数据过多内存溢出优化

问题:

我们知道Android虚拟机给每个应用分配的运行时内存是一定的,一般性能不太好的机器只有16M,好一点的可能也就是64M的样子,假如说我们现在要浏览的新闻总数为一万条,即便是网络很好的情况下,我们可以很快的加载完毕,但是多数情况下也会出现内存溢出从而导致应用崩溃的情况。

解决:

实际上,分批加载也不能完全解决问题,因为虽然我们在分批中一次只增加20条数据到List集合中,然后再刷新到ListView中去,假如有10万条数据,如果我们顺利读到最后这个List集合中还是会累积海量条数的数据,还是可能会造成OOM的情况,这时候我们就需要用到分页,比如说我们将这10万条数据分为1000页,每一页100条数据,每一页加载时都覆盖掉上一页中List集合中的内容,然后每一页内再使用分批加载,这样用户的体验就会相对好一些。

相关文章

  • Android开发欢迎页点击跳过倒计时进入主页

    Android开发欢迎页点击跳过倒计时进入主页

    没点击跳过自然进入主页,点击跳过之后立即进入主页,这个功能怎么实现呢,本文通过实例代码给大家介绍Android开发欢迎页点击跳过倒计时进入主页,感兴趣的朋友一起看看吧
    2023-12-12
  • Android自定义控件实现随手指移动的小球

    Android自定义控件实现随手指移动的小球

    这篇文章主要为大家详细介绍了Android自定义控件实现随手指移动的小球,随着手指触摸移动而移动的效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Android中深入学习对象的四种引用类型

    Android中深入学习对象的四种引用类型

    这篇文章主要介绍Android中深入学习对象的四种引用类型,Java中,一切被视为对象,引用则是用来操纵对象的;在JDK1.2就把对象引用分为四种级别,从而使程序能更灵活控制它的生命周期,级别由高到底依次为强引用、软引用、弱引用、虚引用,需要的朋友可以参考一下
    2021-10-10
  • Android中Uri和Path之间的转换的示例代码

    Android中Uri和Path之间的转换的示例代码

    本篇文章主要介绍了Android中Uri和Path之间的转换的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • Android使用LinearLayout设置边框

    Android使用LinearLayout设置边框

    这篇文章主要介绍了Android如何使用LinearLayout设置边框,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • 自定义视图View绘图基础之Path的使用

    自定义视图View绘图基础之Path的使用

    这篇文章主要介绍了自定义视图View绘图基础之Path的使用,path类是一个非常有用的类,他可以预先在view上讲N个点连成一条“路径”,然后调用Canvas的drawPath(path,paint)即可沿着路径绘制图形,需要的朋友可以参考下
    2023-04-04
  • Android实现下载文件功能的方法

    Android实现下载文件功能的方法

    这篇文章主要介绍了Android实现下载文件功能的方法,对于Android初学者有一定的借鉴价值,需要的朋友可以参考下
    2014-07-07
  • android实现简单进度条ProgressBar效果

    android实现简单进度条ProgressBar效果

    这篇文章主要为大家详细介绍了android实现简单进度条ProgressBar效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • 非常详细的android so库逆向调试教程

    非常详细的android so库逆向调试教程

    这篇文章主要给大家介绍了关于android so库逆向调试的相关资料,文中通过示例代码以及图文介绍的非常详细,对各位Android开发者具有一定的参考学习价值,需要的朋友可以参考下
    2021-08-08
  • Android-Zxing实现二维码的扫描与生成

    Android-Zxing实现二维码的扫描与生成

    本文主要介绍了Android中Zxing实现二维码的扫描与生成的方法,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02

最新评论