listview的优化

本文详细介绍ListView的优化策略,包括convertView的复用、ViewHolder模式的应用、OnScrollListener的合理配置及减少findViewById调用等技巧,旨在提升用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


ListView一般优化


  1. 首先,虽然大家都知道,还是提一下,利用好 convertView 来重用 View,切忌每次 getView() 都新建。ListView 的核心原理就是重用 View。ListView 中有一个回收器,Item 滑出界面的时候 View 会回收到这里,需要显示新的 Item 的时候,就尽量重用回收器里面的 View。
  2. 利用好 View Type,例如你的 ListView 中有几个类型的 Item,需要给每个类型创建不同的 View,这样有利于 ListView 的回收,当然类型不能太多;
  3. 尽量让 ItemView 的 Layout 层次结构简单,这是所有 Layout 都必须遵循的;
  4. 善用自定义 View,自定义 View 可以有效的减小 Layout 的层级,而且对绘制过程可以很好的控制;
  5. 尽量能保证 Adapter 的 hasStableIds() 返回 true,这样在 notifyDataSetChanged() 的时候,如果 id 不变,ListView 将不会重新绘制这个 View,达到优化的目的;
  6. 每个 Item 不能太高,特别是不要超过屏幕的高度,可以参考 Facebook 的优化方法,把特别复杂的 Item 分解成若干小的 Item,特别推荐看一下这个文章:code.facebook.com/posts
  7. 为了保证 ListView 滑动的流畅性,getView() 中要做尽量少的事情,不要有耗时的操作。特别是滑动的时候不要加载图片,停下来再加载,这个库可以帮助你 Glide:github.com/bumptech/gli
  8. 使用 RecycleView 代替。 ListView 每次更新数据都要 notifyDataSetChanged(),有些太暴力了。RecycleView 在性能和可定制性上都有很大的改善,推荐使用。
  9. 有时候,需要从根本上考虑,是否真的要使用 ListView 来实现你的需求,或者是否有其他选择?



ListView与BaseAdapter优化


  1. ListView使用BaseAdapter作适配器的时候,在初始化获取View或者滚动获取View时,都会调用getView方法返回View添加到ListView中,这也是ListView每一项的item。

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

    所传递的三个参数中,

    position表示所添加的view的位置,parent一般为listview。

    而convertView参数:

    1. 如果listView的layout_height设置为wrap_content时,除了position=0时convertView为null,其他时候convertView都不为空。(因为这样会疯狂调用getView方法,不推荐)

    2. 如果listView的layout_height设置为fill_parent或者指定高度时,当listView没有填充到所需高度时,每一个convertView都为null,后面都不为null。

      因为listView所需的全部view不可能全部加载到内存中,所以不需要显示的view就需要回收,回收的view即为convertView。
      以listview的高度为fill_parent为例:

    3. 当convertView为null时,每个item的View需要通过LayoutInflater实例化返回。
    4. 当convertView不为null时,如果整个listView的item使用的是一样的布局,那我们可以直接使用这个view,只需更新convertView中的数据即可。
  2. ViewHolder

    经常在文章中看到ViewHolder来优化ListView,但其实ViewHolder不是库函数,而是需要自己定义的类。(注意viewHolder里面item方法重绘:如invalidate,setVisiblity,requestLayout后,会调用adapter的getView方法

    使用ViewHolder的原因是findViewById方法耗时较大,如果控件个数过多,会严重影响性能,而使用ViewHolder主要是为了可以省去这个时间。通过setTag,getTag直接获取View。

    class  ViewHolder{
        ImageView img;
        TextView name;
    }
    
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if(convertView==null){
            convertView = inflater.inflate(R.layout.list_item, parent, false);
            holder.img = (ImageView) convertView.findViewById(R.id.img);
            holder.name = (TextView) convertView.findViewById(R.id.name);
            holder = new ViewHolder();
            convertView.setTag(holder);
        }else{
            holder = (ViewHolder) convertView.getTag();
        }
        //设置holder
        holder.img.setImageResource(R.drawable.ic_launcher);
        holder.name.setText(list.get(position).partname);
        return convertView;
    }
    
  3. OnScrollListener

    ListView经常需要展示图片,如果在滑动时对滑动过的每张图片都要加载,会比较占内存。推荐的优化方法是设置OnScrollListener,在滑动完成后再下载当前页面的图片。

    listView.setOnScrollListener(new AbsListView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            switch (scrollState){
                // 用户手指滑动中
                case SCROLL_STATE_TOUCH_SCROLL:
                // 用户手指离开,但滑动动画进行中
                case SCROLL_STATE_FLING:
                    break;
                // 滑动结束
                case SCROLL_STATE_IDLE:
                    int start = listView.getFirstVisiblePosition();
                    int end = listView.getLastVisiblePosition();
                    if(end >= listView.getCount()){
                        end = listView.getCount() - 1;
                    }
                    //展示start-end之间的图片
                    break;
            }
        }
    
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    
        }
    });
    
  4. onClickListener,当ListView的item中有比如button这些子view时,需要对其设置onclickListener,通常的写法是在getView方法中一个个设置,比如

    holder.img.setonClickListener(new onClickListenr)...
    

    但是这种写法每次调用getView时都设置了一个新的onClick事件,效率很低。高效的写法可以直接在ViewHolder中设置一个position,然后viewHolder implements OnClickListenr:

    class  ViewHolder implements OnClickListener{
        int position;
        TextView name;
    
        public void setPosition(int position){
            this.position = position;
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                //XXXX
            }
        }
    }
    
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.list_item, parent, false);
            holder = new ViewHolder();
            holder.name = (TextView) convertView.findViewById(R.id.name);
            holder.name.setOnClickListener(holder);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        //设置holder
        holder.name.setText(list.get(position).partname);
        //设置position
        holder.setPosition(position);
        return convertView;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值