ListView的优化总结

Posted sshhsun-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ListView的优化总结相关的知识,希望对你有一定的参考价值。

ListView作为开发中最常见的控件,大部分app都会引用ListView来展示数据,用于用户交互,正因为ListView过于基础,这么常用,所以作为app优化中保持高响应度的ListView 界面就显得尤为重要了

本文结构 :

1.ListView的运行工作原理
2.convertView的复用优化
3.ViewHolder类的使用
4.基于当前滑动状态的优化


ListView的运行工作原理

先来一张最经典的ListView工作图。

在解释ListView的工作原理之前,我们需要了解一些ListView中的重要机制:
Recycler机制

Recycler机制使用一结合来储存我们之前已经使用过的所谓”废弃”的子项view。结合上图中的示例,在item1被滑动移开当前的屏幕时,会将该item的view收集在Recycler相应的集合中,当作缓存并从ListView中移除相关的子view。

接着结合这个图我们讨论一下ListView的工作过程:
ListView刚开始实现时会要求每一个子项进行绘制,刚开始占满屏幕的每一子项的view都是从定义的item的xml文件中inflate出来的,多少个item子项就会创建多少个view。
随着手指的滑动,当item1完全移出屏幕时,基于Recycler机制,将item1的view给depatch掉,即从listView中取消掉item1的view,同时将item1的view存放于Recycler的集合中,如果第8个item和放入“Recycler”的item的view一致,那么就会使用”Recycler”里面的Item来显示,从而不用再重复inflate一次,这样大大节省了创建View的工作,在需要显示大量数据时显得尤为重要。

想要了解更多的listView原理知识,可以访问基于listView源码的工作原理分析。


convertView的复用优化

基于第一部分的基础工作原理分析,我们认识到Google在设计ListView时精妙之处,接下来我们看一下基于Recycler机制中关于”废弃的view”的优化,在继承了Adapter后,我们会重写其中的getView()方法。其中的第二个参数convertView就是Recycler中使用过的view.

优化:在getview()中给每一个需要显示的item生成一个view,如果convertView是空的,我们就需要inflate一个新的view并返回,如果convertView不为空就需要新的view对象,直接使用convertView中的view就可以。

代码如下:

@Override
public View getView(int position, View convertView, ViewGroup parent) 
    Fruit fruit = getItem(position);
    View view;
    if (convertView == null) 
        view = LayoutInflater.from(getContext()).inflate(resourceId, null);
     else 
        view = convertView;
    
    ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
    TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
    fruitImage.setImageResource(fruit.getImageId());
    fruitName.setText(fruit.getName());
    return view;

ViewHolder类的使用

在上述的代码中我们会发现,getView中的View的每一个组件都需要通过findViewById()得到,在不太复杂的情况下这样当然也可以,可是当需要在view中需要复杂的逻辑判断。需要大量的findViewById()操作,不仅编码繁琐麻烦容易丢失一些组件,同时大量的findViewById操作也会影响效率。

如果我们将item中的所有组件都放在一个类里面,那这样操作时不但可以省下一个一个空间绑定的时间,并且会很全面的绑定。

基于此,我们可以定义个ViewHolder来收集所有组件。
对上面的代码进行改进后:

@Override
public View getView(int position, View convertView, ViewGroup parent) 
    Fruit fruit = getItem(position);
    View view;
    ViewHolder holder;
    if (convertView == null) 
        view = LayoutInflater.from(getContext()).inflate(resourceId, null);
        holder = new ViewHolder(view);
        view.setTag(holder);
     else 
        view = convertView;
        holder=convertView.getTag();
    

    holder.fruitImage.setImageResource(fruit.getImageId());
    holder.fruitName.setText(fruit.getName());
    return view;






//定义的新的class 作为手机UI 组件的工具,并在构造函数中进行组件的绑定。

private class ViewHolder
    private ImageView fruitImage;
    private TextView fruitName;
    public ViewHolder(View view )
        fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
        fruitName = (TextView) view.findViewById(R.id.fruit_name);
    

基于当前滑动状态的优化

getView中操作UI组件的代码工作占用了ListView中的大量工作。
而我们可以判断当前用户对ListView的操作,判断是否要进行ListView的实际更新。

基于什么判断呢?

我们可以给当前的ListView设滑动状态监听器,当ListView处于快速滑动状态时,一般情况下用户不太关心的页面的具体信息。所以我们可以在这个状态下停止UI组件的更新。在组件停止或者点击触动的滑动状态下更新组件的信息。

效果如下:

实现步骤:
1.在Adapter的实现类的getView方法中设置一个状态指示是否在滑动

public View getView(int position, View convertView, ViewGroup parent) 
        // convertView
        Log.i(TAG, position + "-.-.-.-.-.-.-" + convertView);
        PicBean pic = lists.get(position);
        Viewholder holder;
         if (convertView == null) 
         convertView = inflater.inflate(R.layout.item_listview, null);
         holder = new Viewholder(convertView);
         convertView.setTag(holder);

          else 
         holder = (Viewholder) convertView.getTag();
         
        if (!isScroll) 
            holder.name.setText(pic.getName());
            Long ldate = Long.valueOf(pic.getDesc());
            String date = sDateFormat.format(ldate);
            holder.desc.setText(date);

         else 
            holder.name.setText("加载中");
            holder.name.setTag(pic.getName());
            Long ldate = Long.valueOf(pic.getDesc());
            String date = sDateFormat.format(ldate);
            holder.desc.setTag(date);
        

        return convertView;
    

2.在ListView的使用活动中设置listView的滑动监听器OnScrollListener并根据状态设置Adapter实现类中的状态标识,当属于停止状态时,得到当前屏幕中的子项的数目并一一配置UI。

lv_show.setOnScrollListener(new OnScrollListener() 
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) 
                switch (scrollState) 
                case AbsListView.OnScrollListener.SCROLL_STATE_FLING://手指离开屏幕,但屏幕依然滑动
                    adapter.setScrollState(true);
                    break;
                case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:// 停止滚动
                    adapter.setScrollState(false);
                    int count = view.getChildCount();//得到当前屏幕的子项。
                    //一一为子项设置UI组件
                    for (int i = 0; i < count; i++) 
                        TextView name = (TextView) view.getChildAt(i)
                                .findViewById(R.id.name);
                        TextView date = (TextView) view.getChildAt(i)
                                .findViewById(R.id.desc);
                        if (name.getTag() != null) 
                            name.setText((String) name.getTag());
                        
                        if (date.getTag() != null) 
                            date.setText((String) date.getTag());
                        
                    
                    break;
                case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL://手指触碰滑动
                    adapter.setScrollState(true);
                    break;
                
            

本篇是关于ListView的初步分析与优化。
关于ListView的原理以及源码,推荐看看ListView工作原理完全解析,带你从源码的角度彻底理解

以上是关于ListView的优化总结的主要内容,如果未能解决你的问题,请参考以下文章

android-ListView使用技巧以及优化

Android学习总结(十三) ———— ListView 简单用法

在listview中颤动Custompaint:忽略两个手指滚动

ListView使用总结

ListView优化总结--Android

Android ListView 的优化