Android Layoutinflater Depth Analysis brings you a whole new understanding

Source: Internet
Author: User

reprint Please indicate source: http://blog.csdn.net/lmj623565791/article/details/38171465, this article from: http://blog.csdn.net/lmj623565791/ article/details/381714651. Off-Topic

I believe that everyone is not unfamiliar with layoutinflate, especially in the adapter GetView method of the ListView will appear basically, use the inflate method to load a layout for the ListView of each item layout. Inflate has three parameters that I understand when I first came to Android:

Three parameters for inflate (int resource, ViewGroup root, Boolean attachtoroot)

If inflate (LayoutID, null), the height of the outermost control of LayoutID is not effective

If inflate (LayoutID, root, False) is considered to be the same as the above effect

If inflate (LayoutID, root, True) considers this to be the case, the width of the outermost control of the LayoutID is displayed correctly

If you think so, then you have to read this article, because this article first will verify that the above understanding is wrong , and then from the source point of view to explain, and finally from the perspective of ViewGroup and view to explain.

2, practice is the only standard to verify the truth

Below I write a particularly common example to verify that the above understanding is wrong, a particularly simple ListView, each item in a button:

Activity's layout file:

<listview xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http://schemas.android.com/ Tools "Android:id=" @+id/id_listview "android:layout_width=" fill_parent "android:layout_height=" Wrap_content "> </ListView>
Layout file for the ListView item:

<button xmlns:android= "http://schemas.android.com/apk/res/android"    xmlns:tools= "http// Schemas.android.com/tools "    android:id=" @+id/id_btn "    android:layout_width=" 120DP "    android:layout_ height= "120DP" ></Button>

Adapter for the ListView:

Package Com.example.zhy_layoutinflater;import Java.util.list;import Android.content.context;import Android.view.layoutinflater;import Android.view.view;import Android.view.viewgroup;import Android.widget.baseadapter;import Android.widget.button;public class Myadapter extends Baseadapter{private Layoutinflater minflater;private list<string> mdatas;public myadapter (context context, list<string> datas {Minflater = Layoutinflater.from (context); mdatas = Datas;} @Overridepublic int GetCount () {return mdatas.size ();} @Overridepublic Object getItem (int position) {return mdatas.get (position);} @Overridepublic long Getitemid (int position) {return position;}  @Overridepublic view GetView (int position, view Convertview, ViewGroup parent) {Viewholder holder = null;if (Convertview = = NULL) {holder = new Viewholder (); Convertview = Minflater.inflate (R.layout.item, null);//convertview = Minflater.inflate (R.layout.item, parent, false);//convertview = Minflater.inflate (R.layout.item, parent, true); holdER.MBTN = (Button) Convertview.findviewbyid (R.ID.ID_BTN); Convertview.settag (holder);} Else{holder = (Viewholder) Convertview.gettag ();} Holder.mBtn.setText (Mdatas.get (position)); return Convertview;} Private Final class Viewholder{button mBtn;}}

Main activity:

Package Com.example.zhy_layoutinflater;import Java.util.arrays;import Java.util.list;import android.app.Activity; Import Android.os.bundle;import Android.widget.listview;public class Mainactivity extends Activity{private ListView Mlistview;private myadapter madapter;private list<string> mdatas = arrays.aslist ("Hello", "Java", "Android"); overrideprotected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate); Setcontentview ( R.layout.activity_main); Mlistview = (ListView) Findviewbyid (r.id.id_listview); madapter = new Myadapter (this, mDatas); Mlistview.setadapter (Madapter);}}
Well, I believe that everyone is familiar with this example, there is nothing to say, we mainly focus on the getview inside the inflate that line of code: The following I wrote in turn GetView:
1, Convertview = minflater.inflate (R.layout.item, NULL);
2, Convertview = Minflater.inflate (R.layout.item, parent, false);
3, Convertview = Minflater.inflate (R.layout.item, parent, true);
See separately:

Figure 1:


Figure 2:


Figure 3:

FATAL EXCEPTION:mainjava.lang.UnsupportedOperationException:addView (View, layoutparams) is not supported in Adapterview

Yes, yes, No. Figure 3, the third way of writing will be an error.

From the above three lines of code change, produce 3 different results, you can see

Inflater (resId, NULL) does not correctly handle the value of a wide height, but inflater (Resid,parent,false) is not the same as inflater (resId, NULL), and it can be seen that the perfect display is the width and height.

and Inflater (resid,parent,true) error (wrong reason in parsing the source of the time said).

This shows: The article began to put forward the understanding is absolutely wrong.

3. Source Code Analysis

Below I through the source to explain, these three kinds of writing true difference

These three methods will eventually execute the following code:

 Public View Inflate (Xmlpullparser parser, ViewGroup root, Boolean attachtoroot) {synchronized (Mconstructorargs)            {final AttributeSet attrs = xml.asattributeset (parser);            Context Lastcontext = (context) mconstructorargs[0];            Mconstructorargs[0] = Mcontext;            View result = root;                try {//Look for the root node.                int type; while (type = Parser.next ())! = Xmlpullparser.start_tag && Type! = Xmlpullparser.end_docum                    ENT) {//Empty} if (type! = Xmlpullparser.start_tag) {                throw new Inflateexception (parser.getpositiondescription () + ": No start tag found!");                                } final String name = Parser.getname ();                    if (DEBUG) {System.out.println ("**************************"); System.out.println ("Creating root view:" + name);                System.out.println ("**************************");                        } if (Tag_merge.equals (name)) {if (root = null | |!attachtoroot) { throw new Inflateexception ("<merge/> can be used only with a valid" + "Viewgrou                    P Root and Attachtoroot=true ");                } rinflate (parser, Root, attrs, false);                    } else {//temp is the root view and was found in the XML view Temp;                    if (tag_1995.equals (name)) {temp = new Blinklayout (Mcontext, attrs);                    } else {temp = Createviewfromtag (root, name, attrs);                    } viewgroup.layoutparams params = null;           if (root = null) {if (DEBUG) {                 System.out.println ("Creating params from root:" + root); }//Create layout params that match root, if supplied params =                        Root.generatelayoutparams (ATTRS);                            if (!attachtoroot) {//Set the layout params for temp if we is not Attaching.                        (If We are, we use AddView, below) temp.setlayoutparams (params); }} if (DEBUG) {System.out.println ("-----> Start Inflatin                    G Children ");                    }//Inflate all children under temp rinflate (parser, temp, attrs, true);                    if (DEBUG) {System.out.println ("-----> Done inflating Children"); }//We are supposed to attachAll of the views we found (Int. temp)//to root.                    Do it now.                    if (root = null && attachtoroot) {Root.addview (temp, params); }//Decide whether to return the root is passed in or the//top view found I                    n XML.                    if (root = null | |!attachtoroot) {result = temp; }}} catch (Xmlpullparserexception e) {inflateexception ex = new Inflateexcepti                On (E.getmessage ());                Ex.initcause (e);            Throw ex; } catch (IOException e) {inflateexception ex = new Inflateexception (Parser.getposi                Tiondescription () + ":" + e.getmessage ());                Ex.initcause (e);            Throw ex;         } finally {//Don ' t retain static reference on context.       Mconstructorargs[0] = Lastcontext;            MCONSTRUCTORARGS[1] = null;        } return result; }    }

Line 6th: First declares the view result = root;//The final return value is result

Line 43rd executed: temp = Createviewfromtag (root, name, attrs); Created view

Then look directly at 48-59:

if (root==null) {params = Root.generatelayoutparams (attrs);        if (!attachtoroot) {   temp.setlayoutparams (params);}}
As you can see, when Root is null,attachtoroot to False, Layoutparams is set for temp.

Go down and look at line 73-75:

if (root = null && attachtoroot) {Root.addview (temp, params);}
When Root is not true for Null,attachtoroot, TMP is added to root by the params.

Then 78-81 lines:


If root is null, or Attachtoroot is false, the TEMP is assigned to result.

Finally, the result is returned.

As can be seen from the above analysis:

Inflate (resId, NULL) only create temp, return temp

Inflate (resId, parent, false) creates temp and then executes temp.setlayoutparams (params); return temp

Inflate (resId, parent, true) creates temp, then executes Root.addview (temp, params), and finally returns root

By the above has been able to explain:

Inflate (resId, NULL) does not handle the width and height correctly because: layout_width,layout_height is set relative to the parent and must be consistent with the layoutparams of the parent. The getlayoutparams of this temp is null

Inflate (resId, parent,false) can be handled correctly because Temp.setlayoutparams (params), the params is root.generatelayoutparams (attrs); To get.

Inflate (resId, parent,true) not only can be handled correctly, but also has added resId this view to the parent, and the return is the parent, and the above two return values have absolute difference, but also remember the previous example of the article, Myadapter inside of the GetView report error:

Java.lang.UnsupportedOperationException:addView (View, layoutparams) is not supported in Adapterview

This is because the source calls the Root.addview (temp, params), and at this point the root is our Listview,listview for the Adapterview subclass:
directly see the source of Adapterview:
@Override public  void AddView (View child) {        throw new Unsupportedoperationexception ("AddView (view) are not Supported in Adapterview ");  }

You can see why this error has occurred.

4. Further analysisThe above I based on the source of the conclusions may be a hint of confusion, I will write an example to illustrate the conclusions we have concluded:
Main layout file:
<button xmlns:android= "http://schemas.android.com/apk/res/android"    xmlns:tools= "http// Schemas.android.com/tools "    android:id=" @+id/id_btn "    android:layout_width=" 120DP "    android:layout_ height= "120DP"    android:text= "button" ></Button>

Main activity:

Package Com.example.zhy_layoutinflater;import Android.app.listactivity;import Android.os.bundle;import Android.util.log;import Android.view.layoutinflater;import Android.view.view;import Android.view.ViewGroup;public Class Mainactivity extends Listactivity{private layoutinflater minflater; @Overrideprotected void OnCreate (Bundle Savedinstancestate) {super.oncreate (savedinstancestate); minflater = Layoutinflater.from (this); View view1 = minflater.inflate (R.layout.activity_main, NULL); View view2 = Minflater.inflate (R.layout.activity_main, (ViewGroup) Findviewbyid (Android. R.id.content), false); View view3 = Minflater.inflate (R.layout.activity_main, (ViewGroup) Findviewbyid (Android. R.id.content), true); LOG.E ("TAG", "View1 =" + view1  + ", View1.layoutparams =" + View1.getlayoutparams ()); LOG.E ("TAG", "view2 =" + view2  + ", View2.layoutparams =" + View2.getlayoutparams ()); LOG.E ("TAG", "VIEW3 =" + view3  );}}

We can see that our main activity did not execute Setcontentview, only performed the 3 methods of Layoutinflater.
Note: Parent We use activity content area: namely Android.r.id.content, is a framelayout, we in Setcontentview (ResId), In fact, the system will automatically pack a layer of framelayout (id=content).

According to our statement above:

View1 's layoutparams should be null
The layoutparams of view2 should not be null, and is Framelayout.layoutparams
VIEW3 is Framelayout, and the button is added to the activity's content area (because r.id.content represents the Actvity content area)

Here's a look at the output, and the activity display:

07-27 14:17:36.703:e/tag (2911): view1 = [email protected], View1.layoutparams = null07-27 14:17:36.703:e/tag (2911): Vie W2 = [email protected], view2.layoutparams = [email protected]07-27 14:17:36.703:e/tag (2911): view3 = [email protected]

Visible, although we did not execute the Setcontentview, but still can see the control is drawn because

View view3 = Minflater.inflate (R.layout.activity_main, (ViewGroup) Findviewbyid (Android. R.id.content), true);

This method has been implemented within the Root.addview (temp, params); The above has been resolved.

It can also be seen that this is exactly the same as our conjecture, which has fully demonstrated the difference between inflate3 overloaded methods. I believe you will be able to choose the best way to use it later. But the following is prepared from ViewGroup and view point of view, why Layoutparams is null, it can not be processed.

5, from the angle of viewgroup and view to analyze

If you have a certain grasp of custom viewgroup and custom view, you will certainly not be unfamiliar with the Onmeasure method:
What the ViewGroup Onmeasure method does is:

Set the measurement mode and measured values for Childview.

How to set it up? is based on Layoutparams.
If the width of the Childview is: Layoutparams. Match_parent, the set mode is measurespec.exactly and the width is calculated for Childview.
If the width of the childview is fixed (that is, greater than 0), the set mode is measurespec.exactly and the lp.width is directly the width of the childview.
If the width of the Childview is: Layoutparams. Wrap_content, the setting mode is: Measurespec.at_most
The height is similar to the width.

Onmeasure method of view:
The main thing is to calculate the width and height according to the ViewGroup incoming measurement mode and the measured value:
This is generally the process:
If the width of the pattern is at_most: Then calculate the wide value yourself.
If the wide pattern is exactly: use Measurespec.getsize (WIDTHMEASURESPEC) directly;

For the last piece, if not clear, it does not matter, I will be in the custom viewgroup and custom view when the detailed explanation.

This is probably the process, the actual drawing process is certainly more complicated than this, is to show that if the view width and height if set to an accurate value, it must be dependent on layoutparams, so our inflate (resid,null) did not handle the width and height correctly.








Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.