ListView的自定义Adapter的性能优化

        ListView可以说是安卓开发中经常用到的了。常常想让ListView的每个item中显示的东西多一点,比如说显示图片、文字、时间等等,这就需要我们自定义adapter,重写getView方法。本文主要是总结一下adapter中getView方法的优化。

以下是本链接中的内容,也是最常见的getView优化方案。

1. ListView需要设置adapter,它的item是通过adapter的方法getView(int position, View convertView, ViewGroup parent)获得的。

2. ListView中只有第一屏的item需要新建,它的引用会被存在RecycleBin对象内,在拖动时后面的item实际上是重从了之前创建的item。
3. 根据上述,ListView在需要显示item时,最开始第一屏时,getView(int position, View convertView, ViewGroup parent )的第二个参数为null,显示第二屏或者回滚显示第一屏时,getView(int position, View convertView, ViewGroup parent )第二个参数是一个原来缓存的item,我们只需要在getView中把它内部数据更新即可。
4. 如果item结构比较复杂,在更新一个已有的item内部数据的时候,查找item内部每一个元素也需要占用不少资源,所以,可以把这些内部元素的引用缓存起来,直接对其赋值,最有效的方法是把这些引用存到对应的item中,比较好的方法是使用setTag()方法。

据上四条,可得出:
1. 在adapter的getView(int position, View convertView, ViewGroup parent)方法中,每次都new一个新的View返回,是性能最差的,浪费了ListView巧妙设计的一片苦心。
2. 如果item内部元素简单,getView中如果传进来的view不为空,直接为其赋值即可。
3. 如果item内部元素复杂,可以使用Google IO 大会中讨论的方法优化(如下):
public View getView(int position, View convertView, ViewGroup parent) {        
    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);
            convertView.setTag(holder);
    }
    else{
            holder = (ViewHolder)convertView.getTag();
    }
        holder.icon1.setImageResource(R.drawable.icon);
        holder.text1.setText(mData[position]);
        return holder;
}
static class ViewHolder {
        TextView text1;
        ImageView icon1;
}

        前段时间做一个项目的时候ListView中加载了五十多条,显示出来的时候花了接近两秒时间,需要优化。而上诉的方案早已经导入。通过log中查看的时候在ListView的第一屏的同一个position的item加载了十多遍,item的重新加载是导致速度慢的原因。

        这是什么样的情况了,看了网上的资料以后我知道原来没有设置器listview 的布局方式不是fill-parent,而是wrap-content,会计算父控件的高度所以造成了一种反复调用情况,从而次数不确定。更深层次的解释为:

View在Draw的时候分成两个阶段:measure和layout,在measure阶段时主要就是为了计算两个参数:height和width。而且要注意的是,这是个递归的过程,从顶向下,DecorView开始依次调用自己子元素的measure。计算完成这两个参数后就开始layout,最后再是draw的调用。

对于ListView,当然每一个Item都会被调用measure方法,而在这个过程中getView和getCount会被调用,而且看用户的需求,可能会有很多次调用。

而为什么会有很多组次调用呢?

问题就在于在layout中的决定ListView或者它的父元素的height和width属性的定义了。fill_parent会好一点,计算方法会比较简单,只要跟父元素的大小相似就行,但是即使是fill_parent,也不能给View当饭吃,还是要计算出来具体的dip,所以measure还是会被调用,只是可能比wrap_content的少一点。至于自适应的它会一直考量它的宽和高,根据内容(也就是它的子Item)计算宽高。可能这个measure过程会反复执行,如果父元素也是wrap_content,这个过程会更加漫长。

所以,解决方法就是尽量避免自适应,除非是万不得已,固定大小或者填充的效果会比较好一些。

         以上是网上经常所说的解决getView重复调用的方法,ListView中尽量采用fill-parent或者固定大小。除此之外,还想到了另外一种方法,既然重复调用是由于onMeasure和onLayout的调用引起,那么我们在adapt里面的getview中,判断是否在onmeasure里,如果在,那么仅仅mInflater.inflate(R.layout.XXX),然后立刻返回这个convertView;如果不在onmeasure里,那么再去真正的onlayout。

具体的就是,首先自定义要用到的view:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d("onMeasure", "onMeasure");
isOnMeasure = true;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d("onLayout", "onLayout");
isOnMeasure = false;
super.onLayout(changed, l, t, r, b);
}
在自定义的类中写一个isOnMeasure()方法返回 isOnMeasure变量。

再然后:

public View getView(int position, View convertView, ViewGroup parent) {
...
if(parent instanceof YourView){
            if(((YourView) parent).isOnMeasure()){
                return convertView;
            }
        }
...
这样就可以了。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值