ViewStub source code analysis, viewstub source code
ViewStub is a special View. The Android official explanation is: an invisible (GONE) placeholder view with a size of 0, mostly used during runtime
Delayed loading, that is, when a view is really needed. In actual projects, I found that there are generally two scenarios for trial use:
1. A view that needs to be displayed for the first time, such as an introductory item. For example, if a user triggers some operations, an introduction is provided to the user,
This kind of introduction only appears once, and will not appear in subsequent operations. In this case, you can use ViewStub to take a place.
Inflate is required. Generally, the creation of such views is expensive;
2. You do not know which view to load until the running time. For example, you may need to decide whether to display View 1 based on some data returned by the server.
View 2 is displayed. In this case, you can also use ViewStub to hold the position until you can make a decision on the corresponding view in inflate;
For the convenience of the next analysis, I will first paste the xml Code of ViewStub, as shown below:
<ViewStub android: id = "@ + id/stub" android: inflatedId = "@ + id/realView" // configurable, But it generally does not mean android: layout = "@ layout/my_real_view" android: visibility = "GONE" // really no need, don't write useless code android: layout_width = "match_parent" android: layout_height = "40dip"/>
In the code, you can also find this ViewStub through findViewById (R. id. stub). When the real "my_real_view" layout is inflate,
The ViewStub control is deleted from the view hierarchy and replaced with the layout represented by "my_real_view. Note that all
Layout_width/height are used to control the view after inflate, instead of ViewStub. This may be the only exception in the Android system.
The method used in our Java code is officially recommended:
ViewStub stub = (ViewStub) findViewById(R.id.stub);View inflated = stub.inflate();
However, one thing to note here is findViewById (R. id. stub), because we know that stub will be in the view hierarchy after being inflate for the first time.
Delete, so you should make sure that such findViewById will be called only once (this is the case in general), but in some cases you did not expect
It may also be called multiple times. We encountered this problem in the project a few days ago, and NPE appeared. The findViewById of stub was called again.
Once, resulting in NPE (because the id cannot be found in the hierarchy ). A good way is to put findViewById in onCreate.
Will only be executed once, otherwise you should be very careful, add some protection processing on your own.
After talking so much about it, you can start with the code:
Public ViewStub (Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {TypedArray a = context. obtainStyledAttributes (attrs, com. android. internal. r. styleable. viewStub, defStyleAttr, defStyleRes); mInflatedId =. getResourceId (R. styleable. viewStub_inflatedId, NO_ID); // read the value of inflateId from the layout file. If yes, mLayoutResource =. getResourceId (R. styleable. viewStub_layout, 0); // Similarly, read the true layout file. recycle (); a = context. obtainStyledAttributes (attrs, com. android. internal. r. styleable. view, defStyleAttr, defStyleRes); mID =. getResourceId (R. styleable. view_id, NO_ID); // read View_id attribute. recycle (); initialize (context);} private void initialize (Context context) {mContext = context; setVisibility (GONE); // see here, you will understand that, as mentioned in the preceding xml file, you do not need to manually specify android: visibility = "GONE". setWillNotDraw (true); // because ViewStub is only a temporary placeholder, it does not do any painting operations, so open this flag to optimize the painting process and improve performance}
Next, let's take a look at several simple methods:
@ Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension (0, 0); // The implementation is simple, the write size is 0, 0} @ Override public void draw (Canvas canvas) {// no painting} @ Override protected void dispatchDraw (Canvas canvas) {// No need for dispatch}/*** When visibility is set to {@ link # VISIBLE} or {@ link # INVISIBLE}, * {@ link # inflate ()} is invoked and this StubbedView is replaced in Its parent * by the inflated layout resource. after that callto this function are passed * through to the inflated view. ** @ param visibility One of {@ link # VISIBLE}, {@ link # INVISIBLE}, or {@ link # GONE }. ** @ see # inflate () */@ Override @ android. view. remotableViewMethod public void setVisibility (int visibility) {// setVisibility does some special processing if (mInflatedViewRef! = Null) {// weak reference View of a real View = mInflatedViewRef. get (); if (view! = Null) {view. setVisibility (visibility);} else {throw new IllegalStateException ("setVisibility called on un-referenced view");} else {// when no inflate exists, if the setVisibility method is called, if it is not GONE, the super of inflate will be lowered automatically in this case. setVisibility (visibility); if (visibility = VISIBLE | visibility = INVISIBLE) {inflate (); // In the case of non-GONE, the inflate () method is called more, therefore, after obtaining the ViewStub instance, you can call either the // inflate () method or the setVisiblity (VISIBLE/INVISIBLE) method to trigger indirect calls to inflate }}
Finally, let's take a look at the implementation of inflate, the Code is as follows:
/*** Inflates the layout resource identified by {@ link # getLayoutResource ()} * and replaces this StubbedView in its parent by the inflated layout resource. ** @ return The inflated layout resource. **/public View inflate () {final ViewParent viewParent = getParent (); if (viewParent! = Null & viewParent instanceof ViewGroup) {if (mLayoutResource! = 0) {final ViewGroup parent = (ViewGroup) viewParent; final LayoutInflater factory; if (mInflater! = Null) {factory = mInflater;} else {factory = LayoutInflater. from (mContext);} final View view = factory. inflate (mLayoutResource, parent, false); // pay attention to this line. In fact, the inflate method of LayoutInflater is called. Note that the last parameter is false, therefore, the returned // view is the root view in the true layout file instead of the parent. If (mInflatedId! = NO_ID) {view. setId (mInflatedId); // If the layout file specifies an id for the real view, set it here} final int index = parent. indexOfChild (this); parent. removeViewInLayout (this); // locate the position of ViewStub in the parent and delete ViewStub. As you can see, during the inflate process, // ViewStub is really deleted !!! Final ViewGroup. LayoutParams layoutParams = getLayoutParams (); // obtain LayoutParams, which is written in the xml file.
// Attributes such as layout_xxx, although they actually belong to ViewStub, if (layoutParams! = Null) {// but the following uses the LayoutParams to add the real view parent. addView (view, index, layoutParams); // note that the added position is the location where the original ViewStub is to be added,
// So it means completely replaced} else {parent. addView (view, index); // if layoutParams is a null case} mInflatedViewRef = new WeakReference <View> (view); // use view to initialize weak reference if (mInflateListener! = Null) {// use mInflateListener to notify the inflate completion event... MInflateListener. onInflate (this, view);} return view; // return the true view} else {// ViewStub must specify the true layout file !!! Throw new IllegalArgumentException ("ViewStub must have a valid layoutResource") ;}} else {// ViewStub must also have a parent view !!! Throw new IllegalStateException ("ViewStub must have a non-null ViewGroup viewParent") ;}/ *** Specifies the inflate listener to be notified after this ViewStub implements * inflated its layout resource. ** @ param inflateListener The OnInflateListener to define y of successful inflation. ** @ see android. view. viewStub. onInflateListener */public void setOnInflateListener (OnInflateListener infla TeListener) {mInflateListener = inflateListener; // There are two ways to respond to the inflate event. Here, you can set listener ,} // You can also use the more generic method: onFinishInflate method of override real view. /*** Listener used to receive a notification after a ViewStub has successfully * inflated its layout resource. ** @ see android. view. viewStub # setOnInflateListener (android. view. viewStub. onInflateListener) */public static interface OnInflateListener {/*** Invoked after a ViewStub successfully inflated its layout resource. * This method is invoked after the inflated view was added to the * hierarchy but before the layout pass. ** @ param stub The ViewStub that initiated the inflation. * @ param inflated The inflated View. */void onInflate (ViewStub stub, View inflated );}}
ViewStub is also used by Android to improve performance. The reason for this is that ViewStub has a delay in loading (inflate until needed ).
True view), the role of placeholder first, on the other hand is also due to the optimization of the three key processes of measure, layout, draw, because ViewStub's three processes
It is very simple, or it is simply do nothing, so the performance is very good. To sum up, Android recommends that we use ViewStub to optimize layout performance. Of course, it is also suitable for use.
ViewStub's timing is not for you to do this in all layout files. I also mentioned this timing at the beginning. You can also refer to the official Android article:
Http://developer.android.com/training/improving-layouts/loading-ondemand.html
Http://developer.android.com/reference/android/view/ViewStub.html
P.S. This article was supposed to have been completed last weekend. It was boring to go to qiandao Lake outing this weekend in the hotel. I found there was another unfinished article to add here, enjoy...