关于RecyclerView的notifyDataSetChanged

关于RecyclerView的notifyDataSetChanged在很久以前,总觉得ListView的notifyDataSetChanged之类的方法很神奇,数据更新后,调用一下,视图就变了…不过自从知道观察者模式以后就没感觉么神奇了,反而对View的绘制测量一系列精细的计算叹为观止—虽然从某种程度上来说,Android的源代码其实挺臃肿的。后面推出了RecyclerView,但其实更新机制并无不同。就如调用notifyDataSetChanged方法:…

大家好,又见面了,我是你们的朋友全栈君。

在很久以前,总觉得ListView的notifyDataSetChanged之类的方法很神奇,数据更新后,调用一下,视图就变了…

不过自从知道观察者模式以后就没感觉那么神奇了,反而对View的绘制测量一系列精细的计算叹为观止—虽然会生出另一种感觉~~某种程度上来说,Android的源代码其实挺臃肿的。

后面推出了RecyclerView,但其实更新机制并无不同。

就如调用notifyDataSetChanged方法:

//RecyclerView.java
    public abstract static class Adapter<VH extends RecyclerView.ViewHolder> { 
   
    
        private final RecyclerView.AdapterDataObservable mObservable = new RecyclerView.AdapterDataObservable();
        
        public final void notifyDataSetChanged() { 
   
            this.mObservable.notifyChanged();
        }
}

从这种意义上来讲,开发者构造的Adpater就是被观察者,而最终的视图RecyclerView就是观察者,当Adpater数据变动时,RecyclerView会被通知到并根据数据变动视图。

而setAdapter就是两者的订阅关系的建立。

    public void setAdapter(@Nullable RecyclerView.Adapter adapter) { 
   
        this.setLayoutFrozen(false);
        this.setAdapterInternal(adapter, false, true);
        this.processDataSetCompletelyChanged(false);
        this.requestLayout();
    }

其实从这里就可以开始正当地怀疑了,requestLayout方法简直就是明目张胆地说—“视图变化就是我干的!”

我们知道,setAdpater视图确实是有所变化的;我们也知道,requestLayout方法和invalidate方法有所不同,invalidate只会调用onDraw,而requestLayout则会onMeasure、onLayout、onDraw都调用。

至于问为什么?和ViewRootImpl有关。requestLayout和invalidate都会调用父类视图的同名方法,最终到达ViewRootImpl中的同名方法,而ViewRootImpl会根据一些标记来决定是否执行measure/layout/draw流程。

罪魁祸首具体是不是requestLayout,我们承接上面notifyDataSetChanged方法的流程,看一下AdapterDataObservable吧:

    static class AdapterDataObservable extends Observable<RecyclerView.AdapterDataObserver> { 
   
        AdapterDataObservable() { 
   
        }

        public void notifyChanged() { 
   
            for(int i = this.mObservers.size() - 1; i >= 0; --i) { 
                                ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onChanged();
            }
        }
    }

这里明显是在找所有订阅了此Adpater的观察者,并逐个通知。
这些个观察者是谁?在构造方法中找到:

    public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) { 
   
        super(context, attrs, defStyle);
        this.mObserver = new RecyclerView.RecyclerViewDataObserver();
    }

是RecyclerViewDataObserver。
看一下这个类的onChanged方法:

    private class RecyclerViewDataObserver extends RecyclerView.AdapterDataObserver { 
   
        RecyclerViewDataObserver() { 
   
        }

        public void onChanged() { 
   
            RecyclerView.this.assertNotInLayoutOrScroll((String)null);
            RecyclerView.this.mState.mStructureChanged = true;
            RecyclerView.this.processDataSetCompletelyChanged(true);
            if (!RecyclerView.this.mAdapterHelper.hasPendingUpdates()) { 
   
                RecyclerView.this.requestLayout();
            }
        }
    }

//AdapterHelper.java
    final ArrayList<AdapterHelper.UpdateOp> mPendingUpdates;

    boolean hasPendingUpdates() { 
   
        return this.mPendingUpdates.size() > 0;
    }

果然,看到了requestLayout。至于hasPendingUpdates,可以理解为添加删除等操作的标记数量,默认情况下是为0的。

所以requestLayout是得以顺利执行的,那么视图变化就变得顺理成章了。

另外例如notifyItemRangeRemoved、notifyItemRangeInserted之类的方法,会与上面的逻辑有所不同。

比如删除某个Item:

    private class RecyclerViewDataObserver extends RecyclerView.AdapterDataObserver { 
   
    
        public void onItemRangeRemoved(int positionStart, int itemCount) { 
   
            RecyclerView.this.assertNotInLayoutOrScroll((String)null);
            if (RecyclerView.this.mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) { 
   
                this.triggerUpdateProcessor();
            }
        }
    }

这里明显就没有那么直接了,并且条件就变成了判断onItemRangeRemoved方法:

//AdapterHelper.java
    boolean onItemRangeRemoved(int positionStart, int itemCount) { 
   
        if (itemCount < 1) { 
   
            return false;
        } else { 
   
            this.mPendingUpdates.add(this.obtainUpdateOp(2, positionStart, itemCount, (Object)null));
            this.mExistingUpdateTypes |= 2;
            return this.mPendingUpdates.size() == 1;
        }
    }

可以看到,在这种情况下,上面提到的mPendingUpdates的数量是有所变化的,变成了1,那么条件满足,会执行triggerUpdateProcessor方法:

//RecyclerView.java
        void triggerUpdateProcessor() { 
   
            if (RecyclerView.POST_UPDATES_ON_ANIMATION && RecyclerView.this.mHasFixedSize && RecyclerView.this.mIsAttached) { 
   
                ViewCompat.postOnAnimation(RecyclerView.this, RecyclerView.this.mUpdateChildViewsRunnable);
            } else { 
   
                RecyclerView.this.mAdapterUpdateDuringMeasure = true;
                RecyclerView.this.requestLayout();
            }
        }
    }

方法中的第一行判断是否成立?

先说结论:在不设置mHasFixedSize时是无法成立的,

        POST_UPDATES_ON_ANIMATION = VERSION.SDK_INT >= 16;

POST_UPDATES_ON_ANIMATION标记是指安卓系统版本大于4.0即为true;
mIsAttached标记是指当前RecyclerView是否已经依附于Window,在已经需要更新数据的场景下,非首次绘制肯定也是为true了;
只有mHasFixedSize这个标记需要手动设置,并且影响较大:

//RecyclerView.java
    public void setHasFixedSize(boolean hasFixedSize) { 
   
        this.mHasFixedSize = hasFixedSize;
    }

    public boolean hasFixedSize() { 
   
        return this.mHasFixedSize;
    }

主要是影响什么呢?
影响大小的测量,也就是视图宽高的绘制。

//RecyclerView.java
    RecyclerView.LayoutManager mLayout;

    protected void onMeasure(int widthSpec, int heightSpec) { 
   
        if (this.mLayout == null) { 
   
            this.defaultOnMeasure(widthSpec, heightSpec);
        } else { 
   
            if (!this.mLayout.isAutoMeasureEnabled()) { 
   
                if (this.mHasFixedSize) { 
   
                    this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
                    return;
                }
                //省略一些测量代码
            }
             //省略一些测量代码
        }
         //省略一些测量代码
    }


    public abstract static class LayoutManager { 
   
    
        public void onMeasure(@NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state, int widthSpec, int heightSpec) { 
   
            this.mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
        }

isAutoMeasureEnabled默认是为false的,所以会进一步判断mHasFixedSize。

如果mHasFixedSize为true,代表着不必再测量宽高,直接使用默认的宽高或者说之前已经测量好的宽高就可以;如果为false,那么进行其他的测量流程。

也就是说,只有在不影响宽高的情况下,我们设置mHasFixedSize为true。

话说回来,也就是在triggerUpdateProcessor方法中,没有意外情况的话,仍然会执行requestLayout方法。

其他诸如notifyItemRangeInserted之类的方法同样如此。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/148019.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 软件工程师业绩简要描述(优秀员工工作业绩和自我评价)

    1/9JAVA软件工程师简历自我评价具有很强的团队精神,有良好的组织和协调能力,有强烈的集体荣誉感。自学能力强,喜欢钻研新技术,敢于面对和克服困难。熟练使用spring+struts+hibernate整合开发。熟练使用jsp、servlet、jstl、jdbc下的编程开发。熟练使用eclipseide开发工具,熟练掌握tomcat等web容器以及j2ee容器的配置以及部署,能够使用junit进行…

    2022年4月13日
    375
  • html+css实现登录界面

    html+css实现登录界面

    2021年12月16日
    45
  • 液晶显示器测试软件6,屏幕坏点检测工具(Datum pixel repair)

    一般有很多的朋友都是不知道自己的电脑的屏幕是不是有多少个坏点的,或者是不知道怎么去测试,那么现在小编就给你介绍下面的这款屏幕坏点检测工具(Datumpixelrepair)来帮助你测试吧!Datumpixelrepair介绍Datumpixelrepair是一款可以快速检测出液晶显示器坏点的位置,用户点击不同的颜色填充整个屏幕,颜色包括:白、黑、红、绿等,使用户更容易检测出坏点!屏幕坏…

    2022年4月7日
    636
  • 金三银四,教你编写一份脱颖而出的简历,从而 offer 手到擒来,要参加校招的同学要提前准备了「建议收藏」

    金三银四,教你编写一份脱颖而出的简历,从而 offer 手到擒来,要参加校招的同学要提前准备了「建议收藏」最近一段时间不是金三银四嘛,有不少同学通过CSDN私信的方式把简历发给我,要我给出一些建议。看多了以后,我发现,有些同学根本不知道怎么写简历,格式乱七八糟,填写的个人经历就像是记流水账一样,完全勾不起我的任何兴趣。那,招聘方应该比我更苛刻,所以这样的简历投递出去,石沉大海的几率很大啊。2014年,我从苏州回到了洛阳,那是我最后一次投简历,现在还在招聘网站上挂着——公开的。嗯,在这么多年的时间里,并没有招聘方和猎头通过这份挂出去的简历给我打电话,虽然我的联系方式一直没变。那只能说明一点,我当时写的简历

    2022年5月2日
    33
  • 关于FEC驱动_FEC伍丰

    关于FEC驱动_FEC伍丰转载说是网络,其实是网卡驱动。而且是针对于FREESCALE芯片的FEC端的驱动,我不知道别的芯片厂商的FEC模块是怎么样的,但就我接触过的几款FREESCALE的芯片来看,比如基于POWERPC的860T和ARM系列的MX27等,他们的FEC有一个明显的特点就是都是由BD和一个DMA缓冲组成,而这个DMA是专用的,也就是只是给FEC使用,区别于芯片内的DMAC模块。我们先来从fec.c这个与

    2022年8月11日
    3
  • 什么是SOA架构?为什么使用SOA架构?

    什么是SOA架构?为什么使用SOA架构?SOA架构简介面向服务的架构(SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)进行拆分,通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种这样的系统中的服务可以以一种统一和通用的方式进行交互**SOA具有以下五个特征**1.可重用;2.松耦合;3.明确定义的接口;…

    2022年6月24日
    27

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号