catalogue

  • 01. What is a ViewStub
  • 02.ViewStub constructor
  • [Inflate ()
  • 04. WeakReference use
  • 05. Why does the ViewStub have no size
  • 06. Why not draw the ViewStub
  • 07. Can you inflate() more than once
  • 08.ViewStub does not support Merge
  • 09.ViewStub usage scenario
  • 10.ViewStub summary analysis

Good news

  • Summary of blog notes [March 2016 to present], including Java basic and in-depth knowledge points, Android technology blog, Python learning notes, etc., including the summary of bugs encountered in daily development, of course, I also collected a lot of interview questions in my spare time, updated, maintained and corrected for a long time, and continued to improve… Open source files are in Markdown format! At the same time, ALSO open source life blog, from 12 years, accumulated a total of N [nearly 1 million words, gradually moved to the Internet], reprint please indicate the source, thank you!
  • Link address:Github.com/yangchong21…
  • If you feel good, you can star, thank you! Of course, also welcome to put forward suggestions, everything starts from small, quantitative change causes qualitative change!

01. What is a ViewStub

  • A ViewStub is an invisible, non-sized View that does not occupy a layout position and can be used to lazily load a layout.
  • When the ViewStub becomes visible orinflate()The layout is loaded (replacing the ViewStub). Therefore, the ViewStub remains in the view hierarchy until it is calledsetVisibility(int)inflate().
  • After the ViewStub is loaded, it is removed and the space it occupies is replaced with a new layout.

02.ViewStub constructor

  • Let’s look at the constructor first:
    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context);
    
        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ViewStub, defStyleAttr, defStyleRes);
        // Id of the layout to be loaded
        mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
        // The layout to be loaded
        mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
        / / ViewStub Id
        mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
        a.recycle();
    
        // The initial state is GONE
        setVisibility(GONE);
        // Set to not draw
        setWillNotDraw(true);
    }
    Copy the code
  • Next we’ll look at the key methods, and then at the initialization state setVisibility method.
    // override the setVisibility(int) method
    @Override
    @android.view.RemotableViewMethod
    public void setVisibility(int visibility) {
        // private WeakReference<View> mInflatedViewRef;
        // mInflatedViewRef is a weak reference to the layout
        if(mInflatedViewRef ! =null) {
            // If it is not null, the View is lazily loaded
            View view = mInflatedViewRef.get();
            if(view ! =null) {
                // Then use setVisibility directly on the View
                view.setVisibility(visibility);
            } else {
                // If null, an exception is thrown
                throw new IllegalStateException("setVisibility called on un-referenced view"); }}else {
            super.setVisibility(visibility);
            // As mentioned earlier, setVisibility(int) can also be used to load layouts
            if (visibility == VISIBLE || visibility == INVISIBLE) {
                // Because this is the inflate() callinflate(); }}}Copy the code

[Inflate ()

  • The core is coming, and when you’re using it, you’ll call this method a lot. Inflate () is the key loading implementation, and the code looks like this:
    public View inflate(a) {
        // Get the superview
        final ViewParent viewParent = getParent();
        
        if(viewParent ! =null && viewParent instanceof ViewGroup) {
            // If no layout is specified, an exception is thrown
            if(mLayoutResource ! =0) {
                // viewParent must be ViewGroup
                final ViewGroup parent = (ViewGroup) viewParent;
                final LayoutInflater factory;
                if(mInflater ! =null) {
                    factory = mInflater;
                } else {
                    // If LayoutInflater is not specified
                    factory = LayoutInflater.from(mContext);
                }
                // Get the layout
                final View view = factory.inflate(mLayoutResource, parent,
                        false);
                // Set the Id for the view
                if(mInflatedId ! = NO_ID) { view.setId(mInflatedId); }// Calculate the position of ViewStub in parent
                final int index = parent.indexOfChild(this);
                // Remove the ViewStub from parent
                parent.removeViewInLayout(this);
                
                // Add view to parent's index position
                final ViewGroup.LayoutParams layoutParams = getLayoutParams();
                if(layoutParams ! =null) {
                    // If ViewStub's layoutParams is not empty
                    // Set it to view
                    parent.addView(view, index, layoutParams);
                } else {
                    parent.addView(view, index);
                }
                
                // mInflatedViewRef is where the view is weakly referenced
                mInflatedViewRef = new WeakReference<View>(view);
    
                if(mInflateListener ! =null) {
                    / / callback
                    mInflateListener.onInflate(this, view);
                }
    
                return view;
            } else {
                throw new IllegalArgumentException("ViewStub must have a valid layoutResource"); }}else {
            throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent"); }}Copy the code
  • Public view
    • A ViewStub can only be synchronized once, after which the ViewStub object will be empty. That is, after a layout specified by the ViewStub is Inflate, it cannot be controlled through the ViewStub.
    • The ViewStub can only be used to Inflate a layout file, not a specific View, or you can Inflate a View into a layout file.

04. WeakReference use

  • The weak reference managed object is created using the code shown below
    • The get method is used here
    @Override
    @android.view.RemotableViewMethod(asyncImpl = "setVisibilityAsync")
    public void setVisibility(int visibility) {
        if(mInflatedViewRef ! =null) {
            View view = mInflatedViewRef.get();
            if(view ! =null) {
                view.setVisibility(visibility);
            } else {
                throw new IllegalStateException("setVisibility called on un-referenced view"); }}else{}}Copy the code
    • Here a weak reference object is created
    public View inflate(a) {
        final ViewParent viewParent = getParent();
        if(viewParent ! =null && viewParent instanceof ViewGroup) {
            if(mLayoutResource ! =0) {
                mInflatedViewRef = new WeakReference<>(view);
                return view;
            } else {
                throw new IllegalArgumentException("ViewStub must have a valid layoutResource"); }}}Copy the code

05. Why does the ViewStub have no size

  • First, take a look at the source code, as follows:
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(0.0);
    }
    
    @Override
    public void draw(Canvas canvas) {}@Override
    protected void dispatchDraw(Canvas canvas) {}Copy the code
  • Do you feel different
    • Draw and dispatchDraw are rewritten, but look at the code and do nothing! And onMeasure does nothing else, just setMeasuredDimension(0,0); A view stub is a special view that does not display any content and is not visible to the layout at load time.

06. Why not draw the ViewStub

  • The setWillNotDraw(true) method looks like this:
    public void setWillNotDraw(boolean willNotDraw) {
        setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
    }
    Copy the code
  • In View, WILL_NOT_DRAW is defined like this:
    /**
     * This view won't draw. {@link #onDraw(android.graphics.Canvas)} won't be
     * called and further optimizations will be performed. It is okay to have
     * this flag set and a background. Use with DRAW_MASK when calling setFlags.
     * {@hide} * /
    static final int WILL_NOT_DRAW = 0x00000080;
    Copy the code
  • After WILL_NOT_DRAW is set, onDraw() is not called, optimizing performance by skipping the drawing process. In ViewGroup, initialize with WILL_NOT_DRAW as follows:
    public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
     
        initViewGroup();
        initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
    }
     
    private void initViewGroup(a) {
        // ViewGroup doesn't draw by default
        if(! debugDraw()) { setFlags(WILL_NOT_DRAW, DRAW_MASK); } mGroupFlags |= FLAG_CLIP_CHILDREN; mGroupFlags |= FLAG_CLIP_TO_PADDING; mGroupFlags |= FLAG_ANIMATION_DONE; mGroupFlags |= FLAG_ANIMATION_CACHE; mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
            mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
        }
     
        setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
     
        mChildren = new View[ARRAY_INITIAL_CAPACITY];
        mChildrenCount = 0;
     
        mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
    }
    Copy the code
  • So, if you need to call onDraw() when writing a custom layout, you need to call setWillNotDraw(false) at initialization. For a closer look at the WILL_NOT_DRAW code in View, check out the PFLAG_SKIP_DRAW code.

07. Can you inflate() more than once

  • The ViewStub object can be synchronized only once, after which the ViewStub object is empty. The e problem to note is that after the ViewStub object is inflate, it cannot be inflate anymore, otherwise an error is reported: the ViewStub must have a non-null ViewGroup viewParent.
  • In fact, look at the source code is very good to understand:
    public View inflate(a) {
        // Get the parent container object of the viewStub
        final ViewParent viewParent = getParent();
    
        if(viewParent ! =null && viewParent instanceof ViewGroup) {
            if(mLayoutResource ! =0) {
                final ViewGroup parent = (ViewGroup) viewParent;
                // Load the layout and give it an ID
                // Layouts are loaded using Layoutinflaters
                final View view = inflateViewNoAdd(parent);
                // This line of code is very important and will be seen below
                replaceSelfWithView(view, parent);
    
                // Use weak references
                mInflatedViewRef = new WeakReference<>(view);
                if(mInflateListener ! =null) {
                    mInflateListener.onInflate(this, view);
                }
                return view;
            } else {
                // The exception inflate will be thrown again if it is already loaded
                throw new IllegalArgumentException("ViewStub must have a valid layoutResource"); }}else {
            throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent"); }}Copy the code
  • You can actually see it in a graph, like this, from the web
  • That is, the ViewStub becomes null once the method above the inflate is called, so null Pointers are a special concern when using this object.

08.ViewStub does not support Merge

  • Cannot import a layout that contains the merge tag into the ViewStub. Otherwise an error: android. View. InflateException: Binary XML file line # 1: can be used only with a valid ViewGroup root and attachToRoot=true

09.ViewStub usage scenario

  • Most apps have such a function: display an empty view when the loaded data is empty, display the UI corresponding to the loading failure when the data fails to load, load the UI without the network when there is no network, and support click retry. The user experience will be better than the blank screen. Commonly known as page status switch management…… Generally speaking, the UI style of loading, loading failure, empty data and other states needs to be consistent in all pages of the App, that is, it needs to achieve global unity and also support local customization.
  • The advantage of the ViewStub is that it is not necessary to display all the content in the above scenario. You can hide some views and load them into the current Layout when the user needs to display them. In this case, you can use the ViewStub control to reduce resource consumption. Make the initial load faster.
  • Then there is the open source library of the state manager used in the previous development, which uses the ViewStub control to completely separate the View state switch from the Activity. With Builder mode from the need to add state View, can be set with data, data is empty, load data error, network error, load medium a variety of states, and support custom state layout. It can be said that performance is not affected at all…

10.ViewStub summary analysis

  • Analysis of the principle of source code, no matter which step is recognized, the ultimate goal is still in use, that is, the knowledge obtained from the source code is used in the actual development, so about the use of ViewStub skills, specific can see my state manager case, link address: github.com/yangchong21…
  • Welcome to star, which is also the source of open source and blogging, haha

ViewStub state management library:Github.com/yangchong21…

Open source blog summary:Github.com/yangchong21…