Deep analysis of Android app's layoutinflate layout _android

Source: Internet
Author: User
Tags getmessage

1, digression
I believe that everyone is not unfamiliar with the layoutinflate, especially in the ListView adapter GetView method of the basic will appear, using the inflate method to load a layout for the ListView of the layout of each item. Inflate has three parameters, which I understood when I was a beginner of Android:
(1) for inflate three parameters (int resource, ViewGroup root, Boolean attachtoroot);
(2) If inflate (LayoutID, NULL) The LayoutID of the outermost control of the height is not effective;
(3) If the inflate (LayoutID, root, False) is considered to be the same as the above effect;
(4) If Inflate (LayoutID, Root, True), then the layoutid of the outermost control is considered to be normal;
If you think so, then you have to read this article carefully, because this article will first 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 truth
below I write a particularly common example to verify that the above understanding is wrong, a particularly simple ListView, put a button in each item:
The layout file for the activity:

<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> 

ListView the item's 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" > 
 
</Button> 

ListView Adapters:

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; 
  @Override public int GetCount () {return mdatas.size (); 
  @Override public Object getitem (int position) {return mdatas.get (position); 
  @Override public long getitemid (int position) {return position; @Override public 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"); 
 
  @Override 
  protected 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 we are all familiar with this example, there is nothing to say, we mainly focus on the getview inside the inflate 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 the effect chart separately:

Figure 1:

Figure 2:

Figure 3:

FATAL exception:main 
java.lang.UnsupportedOperationException:  
addview (View, layoutparams) is not supported In Adapterview 

Yes, I do. No Figure 3, the third type of writing will be an error.
From the above three lines of code changes, resulting in 3 different results, you can see
Inflater (RESID, NULL) does not correctly handle a wide-high value, but Inflater (Resid,parent,false) is not consistent with inflater (RESID, NULL), and it can be seen that the perfect display width and height.
and Inflater (resid,parent,true) error (the 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 analysis
below I explained through the source code, 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_document) { 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 inflate 
          Exception ("<merge/> can be used to with a valid" + "ViewGroup root and Attachtoroot=true"); 
        } rinflate (parser, Root, attrs, false); 
          else {//temp is the root view, that's 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 are not//attaching. 
            (If We are, we use AddView, below) temp.setlayoutparams (params); 
          } if (DEBUG) {System.out.println ("-----> Start inflating Children"); 
          }//Inflate all children under temp rinflate (parser, temp, attrs, true); 
          if (DEBUG) {System.out.println ("-----> Done inflating Children"); }//We are supposed to attach all views We found (int temp)//to root. 
          do so now. 
          if (root!= null && attachtoroot) {Root.addview (temp, params); 
          }//Decide whether to return the root this is passed in or the//top view found in XML. 
          if (root = null | |!attachtoroot) {result = temp; (Xmlpullparserexception e) {inflateexception ex = new InflateexCeption (E.getmessage ()); 
        Ex.initcause (e); 
      Throw ex; The catch (IOException e) {inflateexception ex = new Inflateexception (Parser.getpositiondescription ( 
        + ":" + e.getmessage ()); 
        Ex.initcause (e); 
      Throw ex; 
        finally {//Don ' t retain static reference on the context. 
        Mconstructorargs[0] = Lastcontext; 
      MCONSTRUCTORARGS[1] = null; 
    return result; 

 } 
  }

Line 6th: First declare view result = root;//final return value is
Line 43rd executes: temp = Createviewfromtag (root, name, attrs); CREATE view
Then look directly at 48-59:

if (root!=null) 
{ 
 params = Root.generatelayoutparams (attrs); 
    if (!attachtoroot) 
 { 
  temp.setlayoutparams (params); 
 } 
} 

You can see that the layoutparams is set for temp when Root is not null,attachtoroot to false.
Go down and look at line 73-75:

if (root!= null && attachtoroot) 
{ 
Root.addview (temp, params); 
} 

When Root is not null,attachtoroot to true, the TMP is added to root by params.
And then 78-81 lines:

if (root = null | |!attachtoroot) {result  
= temp;  
}  

If root is null, or attachtoroot is False, temp is assigned to result.
Finally, result is returned.

As you can see from the above analysis:
(1) Inflate (resid, NULL) only creates temp, returns TEMP
(2) Inflate (Resid, parent, false) creates temp, then executes temp.setlayoutparams (params);
(3) Inflate (Resid, parent, true) creates temp, then executes Root.addview (temp, params), and finally returns root
From the above has been able to explain:
(1) Inflate (RESID, NULL) does not handle 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. And the getlayoutparams of this temp is null
Inflate (Resid, parent,false) can be handled correctly because Temp.setlayoutparams (params); this params is root.generatelayoutparams (attrs); To get.
(2) Inflate (Resid, parent,true) not only can handle correctly, but also has resid this view added to the parent, and returned is the parent, and the above two return values are absolutely different, remember the article in front of the example, Myadapter inside the GetView newspaper error:
[HTML] View plain copy on the code to see a piece of a snippet derived from my Code

Java.lang.UnsupportedOperationException:  
AddView (View, layoutparams) is isn't supported in Adapterview 

This is because the source code calls Root.addview (temp, params), and at this point root is our Listview,listview Adapterview subclass:
Look directly at the source of Adapterview:

@Override public 
 void AddView (View child) { 
    throw new unsupportedoperationexception ("AddView View") Supported in Adapterview "); 
 } 

You can see why this error has occurred.
4. Further analysis
above I based on the source of the conclusions may be a trace of confusion, I will write an example to illustrate the conclusions we have reached:
Master 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; 
 
    @Override protected 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 3 methods of Layoutinflater were executed.
Note: Parent we are using the content area of the activity: that Android.r.id.content, is a framelayout, when we were Setcontentview (RESID), In fact, the system will automatically pack a layer of framelayout (id=content).
According to our above statement:
(1) The layoutparams of view1 should be null
(2) The layoutparams of the view2 should not be null and framelayout.layoutparams
(3) VIEW3 is framelayout and adds this button to the content area of the activity (because R.id.content represents actvity content area)
Here's a look at the output, and the presentation of the activity:

07-27 14:17:36.703:e/tag (2911): View1 = android.widget.button@429d1660, view1.layoutparams = null 
07-27 14:17:36.703:e/tag (2911): View2 = android.widget.button@42a0e120, View2.layoutparams = android.widget.framelayout$ Layoutparams@42a0e9a0 
07-27 14:17:36.703:e/tag (2911): VIEW3 = android.widget.framelayout@42a0a240 

Effect Chart:

Visible, although we do not perform setcontentview, we can still see the drawn controls because

Copy Code code as follows:

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

This method has been implemented internally by 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 completely explained the difference between the methods of Inflate3 overloading. I believe you can choose the best way when you use it later. However, the following is prepared from the perspective of ViewGroup and view, why Layoutparams is null, it can not be handled.

5, parsing
from the perspective of ViewGroup and view if you have some control of your custom viewgroup and custom view, you certainly won't be unfamiliar with the Onmeasure method:
ViewGroup's Onmeasure method is to
set the measurement mode and the measured value for Childview.
How do I set it? is according to Layoutparams.
(1) If the width of the Childview is: Layoutparams. Match_parent, the setting mode is measurespec.exactly and the width is calculated for Childview.
(2) If the width of the Childview is: a fixed value (that is, greater than 0), the setting mode is measurespec.exactly and the lp.width is directly used as the width of the childview.
(3) If the width of the Childview is: Layoutparams. Wrap_content, the setting mode is: Measurespec.at_most
The height is similar to the width.
View's Onmeasure method:
The main thing to do is to calculate the width and height of the viewgroup in terms of the measured and measured values passed in:
typically such a process:
(1) If the width of the mode is at_most: Then calculate the width of their own values.
(2) If the wide mode is exactly: direct use of measurespec.getsize (WIDTHMEASURESPEC);
(3) for the last piece, if it's not clear, it doesn't matter, I'll explain in detail when customizing ViewGroup and Customizing View.
This is probably the process, the real drawing process is certainly more complex than this, is to show that if the view width and height if set to the exact value, it must rely on layoutparams, so our inflate (resid,null) did not properly handle width and height.

Related Article

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.