You must have heard of ViewStub, but do you understand these details?

1 What is ViewStub

1. ViewStub is an invisible, no size, and does not occupy the layout position, can be used to lazy load the layout.

2. When the ViewStub becomes visible or inflate (), the layout will be loaded (replace ViewStub). Therefore, the ViewStub stays in the view hierarchy until setVisibility (int) or inflate () is called.

3. After the ViewStub is loaded, it will be removed, and the space it occupies will be replaced by the new layout.

Here I share a 2 month Android advanced interview analysis note document, including knowledge point notes and high frequency interview problem analysis and some knowledge point video explanations for everyone! In order not to affect reading, here is a part of the content shown in the screenshot of the catalog. If you have any trouble, please like it and click the online link below to get a free way to receive it!
Ali P6P7 [Android] Advanced Information Sharing + Essential Job Interview Questions

2 ViewStub construction method

First look at the construction method:

public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context);

    final TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.ViewStub, defStyleAttr, defStyleRes);
    // 要被加载的布局 Id
    mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
    // 要被加载的布局
    mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
    // ViewStub 的 Id
    mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
    a.recycle();

    // 初始状态为 GONE
    setVisibility(GONE);
    // 设置为不会绘制
    setWillNotDraw(true);
}

Next, let's look at the key methods, and then look at the initialization state setVisibility method.

// 复写了 setVisibility(int) 方法
@Override
@android.view.RemotableViewMethod
public void setVisibility(int visibility) {
    // private WeakReference<View> mInflatedViewRef;
    // mInflatedViewRef 是对布局的弱引用
    if (mInflatedViewRef != null) {
        // 如果不为 null,就拿到懒加载的 View
        View view = mInflatedViewRef.get();
        if (view != null) {
            // 然后就直接对 View 进行 setVisibility 操作
            view.setVisibility(visibility);
        } else {
            // 如果为 null,就抛出异常
            throw new IllegalStateException("setVisibility called on un-referenced view");
        }
    } else {
        super.setVisibility(visibility);
        // 之前说过,setVisibility(int) 也可以进行加载布局
        if (visibility == VISIBLE || visibility == INVISIBLE) {
            // 因为在这里调用了 inflate()
            inflate();
        }
    }
}

3 inflate () method analysis

The core is here, and this method is often called when it is used normally. inflate () is the key loading implementation, the code is as follows:

public View inflate() {
    // 获取父视图
    final ViewParent viewParent = getParent();

    if (viewParent != null && viewParent instanceof ViewGroup) {
        // 如果没有指定布局,就会抛出异常
        if (mLayoutResource != 0) {
            // viewParent 需为 ViewGroup
            final ViewGroup parent = (ViewGroup) viewParent;
            final LayoutInflater factory;
            if (mInflater != null) {
                factory = mInflater;
            } else {
                // 如果没有指定 LayoutInflater
                factory = LayoutInflater.from(mContext);
            }
            // 获取布局
            final View view = factory.inflate(mLayoutResource, parent,
                    false);
            // 为 view 设置 Id
            if (mInflatedId != NO_ID) {
                view.setId(mInflatedId);
            }
            // 计算出 ViewStub 在 parent 中的位置
            final int index = parent.indexOfChild(this);
            // 把 ViewStub 从 parent 中移除
            parent.removeViewInLayout(this);

            // 接下来就是把 view 加到 parent 的 index 位置中
            final ViewGroup.LayoutParams layoutParams = getLayoutParams();
            if (layoutParams != null) {
                // 如果 ViewStub 的 layoutParams 不为空
                // 就设置给 view
                parent.addView(view, index, layoutParams);
            } else {
                parent.addView(view, index);
            }

            // mInflatedViewRef 就是在这里对 view 进行了弱引用
            mInflatedViewRef = new WeakReference<View>(view);

            if (mInflateListener != null) {
                // 回调
                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");
    }
}

Inflate use characteristics

1. ViewStub can only be Inflate once. After inflate, the ViewStub object will be set to empty. That is, after a layout specified by ViewStub is Inflate, it can no longer be controlled by ViewStub.

2. ViewStub can only be used to Inflate a layout file, not a specific View, of course, you can also write View in a layout file.

4 WeakReference use

The use of weak reference management object creation, the code is as follows

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 {

    }
}

Created weak reference object here

public View inflate() {
    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");
        }
    } 
}

5 Why does ViewStub have no size

First look at a piece of 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) {
}

Do you feel very different?

Although draw and dispatchDraw have been rewritten, but look at the code, they do nothing! And onMeasure does nothing, directly setMeasuredDimension (0,0); to set the view area to 0, the original one ViewStub is a view However, it is a special view that does not display any content or display any content, and is not visible to the layout when it is loaded.

6 Why doesn't ViewStub draw

Take a specific look at the setWillNotDraw (true) method, the code is as follows:

public void setWillNotDraw(boolean willNotDraw) {
    setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}

In View, the definition of WILL_NOT_DRAW is as follows:

/**
 * 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;

After setting WILL_NOT_DRAW, onDraw () will not be called. By skipping the drawing process, the performance is optimized. In ViewGroup, WILL_NOT_DRAW is set during initialization, the code is 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() {
    // 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;
}

Therefore, when writing a custom layout, if you need to call onDraw () to draw, you need to call setWillNotDraw (false) during initialization. If you want to further read the relevant source code of Will_NOT_DRAW in View, you can take a look at the code related to PFLAG_SKIP_DRAW.

7 can repeatedly inflate () it

The ViewStub object can only be Inflate once, and then the ViewStub object will be set to empty. At the same time, the problem that needs to be noted is that after inflate a ViewStub object, you cannot inflate it again, otherwise you will get an error: ViewStub must have a non-null ViewGroup viewParent.

In fact, looking at the source code is easy to understand:

public View inflate() {
    //获取viewStub的父容器对象
    final ViewParent viewParent = getParent();

    if (viewParent != null && viewParent instanceof ViewGroup) {
        if (mLayoutResource != 0) {
            final ViewGroup parent = (ViewGroup) viewParent;
            //这里是加载布局,并且给它设置id
            //布局的加载是通过LayoutInflater解析出来的
            final View view = inflateViewNoAdd(parent);
            //这行代码很重要,下面会将到
            replaceSelfWithView(view, parent);

            //使用弱引用
            mInflatedViewRef = new WeakReference<>(view);
            if (mInflateListener != null) {
                mInflateListener.onInflate(this, view);
            }
            return view;
        } else {
            //如果已经加载出来,再次inflate就会抛出异常呢
            throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
        }
    } else {
        throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
    }
}

In fact, you can also understand it with a picture, as shown below, taken from the network

That is to say, once the method above is called, the ViewStub will become null, so the use of this object requires special attention to the problem of null pointers.

8 ViewStub does not support merge

You cannot introduce layouts containing merge tags into the ViewStub. Otherwise, an error will be reported:

android.view.InflateException: Binary XML file line #1:  can be used only with a valid ViewGroup root and attachToRoot=true

9 ViewStub usage scenarios

Most of the general apps have such a function. When the loaded data is empty, a view with empty data is displayed, and when the data fails to load, the UI corresponding to the failed load is displayed. When there is no network, the UI without the network is loaded and supported. Clicking to retry will be better than a white screen user experience.

Commonly known as page state switching management ... Generally speaking, the UI style of loading, loading failure, empty data and other states needs to be consistent on all pages in the App, that is, it needs to be globally unified and also supports local customization.

The advantage of ViewStub is that in the above scenario, it is not necessary to display all the content. You can hide some View views and load them into the current Layout when the user needs to display them. At this time, ViewStub can be used. Controls, this can reduce resource consumption and make the initial loading speed faster.

Then there is the open source library of the state manager that was developed and used before. It uses the ViewStub control to completely separate the switching of the View state and the Activity. Use builder mode to freely add the required state View, you can set the data, the data is empty, load data error, network error, load medium and multiple states, and support custom state layout. It can be said that it does not affect performance at all ...

10 ViewStub summary analysis

Analyze the principle of the source code, no matter what step is recognized, the ultimate goal is still in the application, that is, the knowledge obtained by looking at the source code is used in actual development,

Published 488 original articles · praised 85 · 230,000 views +

Guess you like

Origin blog.csdn.net/Coo123_/article/details/104437061