ViewStub use and parse the source code

We know that there are three tabs layout optimization, include, merge and ViewStub. We can extract a common layout xml, and then use to include references; the layout will be different levels, if the inner layer and the upper layer is the same container, you can use the merge, but keep in mind, merge must be in the root node; ViewStub is not a keyword, but a subclass of the View, its role is to account for the pit, lazy loading layout inside it references. Explain in detail ViewStub this class, look at an example to see how it was used
 

layout_content:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <ViewStub
        android:id="@+id/game_over_id"
        android:layout="@layout/layout_game_over"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</FrameLayout>

layout_game_over:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#CC000000"
    android:id="@+id/fl_pc_view"
    >

    <ImageView
        android:id="@+id/iv_pc_close"
        android:layout_width="22dp"
        android:layout_height="22dp"
        android:layout_gravity="right|top"
        android:layout_margin="20dp"
        android:background="@drawable/pc_close" />

    <TextView
        android:id="@+id/tv_pc_desc"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="破产说明"
        />

</FrameLayout>

Activity:

    private void initVS() {
        ViewStub vs = findViewById(R.id.game_over_id);
        View view = vs.inflate();
        ImageView imageView = view.findViewById(R.id.fl_pc_view);
        TextView textView = view.findViewById(R.id.tv_pc_desc);
    }

Note that, in Activity, we can setContentView (R.layout.layout_content); any time after the calling of initVS () method, which based on business needs to determine, call initVS () after, layout_game_over layout will be loaded into layout_content, the benefits of doing so is when layout_content layout initialization, which create and draw the view will be relatively small, this can increase the efficiency of initialization. We look at the code and implement the principle of ViewStub

attrs:
    <declare-styleable name="ViewStub">
        <attr name="id" />
        <attr name="layout" format="reference" />
        <attr name="inflatedId" format="reference" />
    </declare-styleable>

This is a self-defined attributes, wrote custom control should know. Look constructor ViewStub eventually will call the constructor

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

        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ViewStub, defStyleAttr, defStyleRes);
        mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
        mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
        mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
        a.recycle();

        setVisibility(GONE);
        setWillNotDraw(true);
    }

Here to note two points: super first method is called, is to construct an argument, that parent View will not perform read custom properties of methods, so even if you write in xml nodes ViewStub View common properties, not to read, will not work. At the same time, corresponding to the xml mID id, will not be acquired, so ViewStub configuration, where the value of the property after re-reading custom, to mID assignment; two attributes by reading the reference in the xml id value layout layout assigned to the member variables for later use; the last two lines of code to the controls disappear and let the drawing to see it, the way and then look at a few other related methods

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(0, 0);
    }

    @Override
    public void draw(Canvas canvas) {
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
    }

We know that controls are necessary to measure and draw, where the size of the direct forced to 0, and rewrite the draw () and dispatchDraw () method, look carefully removed the super () method, the parent class's draw (Canvas canvas) method also draw back some of the background, where it also gives direct omitted to avoid unnecessary waste of performance.


We look inflate () method, which the code is also a basis for comparison

    public View inflate() {
        final ViewParent viewParent = getParent();

        if (viewParent != null && viewParent instanceof ViewGroup) {
            if (mLayoutResource != 0) {
                final ViewGroup parent = (ViewGroup) viewParent;
                final View view = inflateViewNoAdd(parent);
                replaceSelfWithView(view, parent);

                mInflatedViewRef = new WeakReference<>(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");
        }
    }

Do not look at the outside of the determination, to see which direct method, mLayoutResource is essential, because it is converted by the layout; obtain the current control by the parent container ViewStub getParent () method; See inflateViewNoAdd (parent) method, look name means probably guessed it functions, look at the code

    private View inflateViewNoAdd(ViewGroup parent) {
        final LayoutInflater factory;
        if (mInflater != null) {
            factory = mInflater;
        } else {
            factory = LayoutInflater.from(mContext);
        }
        final View view = factory.inflate(mLayoutResource, parent, false);

        if (mInflatedId != NO_ID) {
            view.setId(mInflatedId);
        }
        return view;
    }

If there mInflater our own set, then use your own, or use LayoutInflater, to convert layout layout by inflate (mLayoutResource, parent, false) method View, pay attention to the third parameter is false, that is not added the parent; the bottom is set ID; the role of this method is that the layout corresponding to the layout mLayoutResource into a single View control, to see the next method replaceSelfWithView (view, parent)

    private void replaceSelfWithView(View view, ViewGroup parent) {
        final int index = parent.indexOfChild(this);
        parent.removeViewInLayout(this);

        final ViewGroup.LayoutParams layoutParams = getLayoutParams();
        if (layoutParams != null) {
            parent.addView(view, index, layoutParams);
        } else {
            parent.addView(view, index);
        }
    }

In this method, find the current position in the index ViewStub parent container, and then removes itself from the parent container; View layout is a layout corresponding to the converted View mLayoutResource The index value and add it to the location specified in the parent vessel, If you get your own LayoutParams property is not null, it is also assigned to the view. See here, we understand, ViewStub accounts for only a pit, a shell company, when you call inflate () method converts the layout, and then give their position required controls. Continue to look inflate () method, after replacing the view, it will join mInflatedViewRef soft references while calling OnInflateListener this listener callback. The outer layer then look judgment found inflate () is called only once, if the second call, since ViewStub parent container has been removed, ViewParent viewParent = getParent () will be null, the exception will be thrown. As setVisibility (int visibility) method, it may be called multiple times, which is the logic we can see for yourself.

    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 {
            super.setVisibility(visibility);
            if (visibility == VISIBLE || visibility == INVISIBLE) {
                inflate();
            }
        }
    }

Source read it, you know why ViewStub merge and can not be used, because inflate () method of LayoutInflater

    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            ...
            View result = root;
            try {
                ...
                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    ...
                }

            } catch (XmlPullParserException e) {
                ...
            }

            return result;
        }
    }

If the merge tag, then method attachToRoot to false, the execution code logic exception is thrown, to pay attention to this point.
 

Published 176 original articles · won praise 11 · views 30000 +

Guess you like

Origin blog.csdn.net/Deaht_Huimie/article/details/104782366