Android LayoutInflater provides a new understanding of layoutinflater.

Source: Internet
Author: User

Android LayoutInflater provides a new understanding of layoutinflater.
Reprinted please indicate the source: http://blog.csdn.net/lmj623565791/article/details/38171465, this article from: http://blog.csdn.net/lmj623565791/article/details/381714651

I believe you are familiar with LayoutInflate, especially in the getView METHOD OF THE ListView Adapter, which usually appears. You can use the inflate method to load a layout for each Item layout of the ListView. Inflate has three parameters, which I understand when I was a beginner at Android:

Three Inflate parameters (int resource, ViewGroup root, boolean attachToRoot)

If inflate (layoutId, null), the width and height of the outermost control of layoutId are ineffective.

If inflate (layoutId, root, false) is considered to be the same as the above

If inflate (layoutId, root, true) is used, the width and height of the outermost layer control of layoutId can be displayed normally.

If you think so, you need to read this article carefully, because this article will first verify the above understanding:Incorrect, And then explain from the source code perspective, and finally explain from the ViewGroup and View perspectives.

2. practice is the only criterion for verifying truth

Below I will write a very common example to verify that the above understanding is wrong. A very simple ListView, put a button in each Item:

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

ListView adapter:

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 everyone is not familiar with this example. We have nothing to say about it. We mainly focus on the inflate line of code in getView: Next I will write the code in getView in sequence:
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:

Figure 1:


Figure 2:


Figure 3:

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

Well, that's right. No figure 3. The third write method will report an error.

The changes in the above three lines of code generate three different results.

Inflater (resId, null) does not correctly handle high-width values, but inflater (resId, parent, false) does not have the same effect as inflater (resId, null, it shows the perfect display of width and height.

Inflater (resId, parent, true) reports an error (the cause of the error is described when the source code is parsed ).

It can be seen that the understanding at the beginning of the article is absolutely false.

3. source code parsing

Below I will explain through the source code, the true differences between the three writing methods

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 InflateException("<merge /> can be used only with a valid "                                + "ViewGroup root and attachToRoot=true");                    }                    rInflate(parser, root, attrs, false);                } else {                    // Temp is the root view that 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 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 the views we found (int temp)                    // to root. Do that now.                    if (root != null && attachToRoot) {                        root.addView(temp, params);                    }                    // Decide whether to return the root that was passed in or the                    // top view found in xml.                    if (root == null || !attachToRoot) {                        result = temp;                    }                }            } catch (XmlPullParserException e) {                InflateException ex = new InflateException(e.getMessage());                ex.initCause(e);                throw ex;            } catch (IOException e) {                InflateException ex = new InflateException(                        parser.getPositionDescription()                        + ": " + e.getMessage());                ex.initCause(e);                throw ex;            } finally {                // Don't retain static reference on context.                mConstructorArgs[0] = lastContext;                mConstructorArgs[1] = null;            }            return result;        }    }

Row 6th: first declared View result = root; // The final return value is result

Line 3 executes: temp = createViewFromTag (root, name, attrs); creates a View

Then let's look at 48-59:

if(root==null){ params = root.generateLayoutParams(attrs);        if (!attachToRoot) {   temp.setLayoutParams(params); }}
We can see that when root is null and attachToRoot is false, LayoutParams is set for temp.

Continue to the next step. See lines:

if (root != null && attachToRoot){root.addView(temp, params);}
When root is not null and attachToRoot is true, tmp is added to root according to params.

Then lines 8-81:

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

If root is null or attachToRoot is false, assign temp to result.

Finally, the result is returned.

From the above analysis, we can see that:

Inflate (resId, null) only creates temp and returns temp

Inflate (resId, parent, false) to create temp, and then execute temp. setLayoutParams (params );

Inflate (resId, parent, true) to create temp, then execute root. addView (temp, params), and finally return root

It can be explained as follows:

Inflate (resId, null) cannot properly process width and height because layout_width and layout_height are set relative to the parent level and must be consistent with LayoutParams of the parent level. The getLayoutParams of temp is null.

Inflate (resId, parent, false) can be handled correctly, because temp. setLayoutParams (params); this params is exactly what root. generateLayoutParams (attrs); gets.

Inflate (resId, parent, true) not only can be correctly processed, but also the resId view has been added to the parent, and the returned parent is definitely different from the returned values, remember the error reported by getView in MyAdapter in the previous example:

java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView

This is because the source code calls root. addView (temp, params). At this time, root is our ListView, and ListView is a subclass of AdapterView:
Directly view the source code of AdapterView:
@Override  public void addView(View child) {        throw new UnsupportedOperationException("addView(View) is not supported in AdapterView");  }

We can see why this error is generated.

4. Further analysis of the conclusions I have drawn from the source code may be confused. I will write another example to illustrate the conclusions we have drawn above:
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 does not execute setContentView, and only executes the three LayoutInflater methods.
Note: We use the Activity content area: android. r. id. content is a FrameLayout. When setContentView (resId) is used, the system will automatically package a layer of FrameLayout (id = content ).

According to the above statement:

LayoutParams of view1 should be null
LayoutParams of view2 should not be null and be FrameLayout. LayoutParams
View3 is FrameLayout and the button is added to the content area of the Activity (because R. id. content represents the content area of Actvity)

Let's take a look at the output result and Activity display:

07-27 14:17:36.703: E/TAG(2911): view1 = android.widget.Button@429d1660 , view1.layoutParams = null07-27 14:17:36.703: E/TAG(2911): view2 = android.widget.Button@42a0e120 , view2.layoutParams = android.widget.FrameLayout$LayoutParams@42a0e9a007-27 14:17:36.703: E/TAG(2911): view3 = android.widget.FrameLayout@42a0a240

:

It can be seen that although setContentView is not executed, the drawn control can still be seen because

View view3 = mInflater. inflate (R. layout. activity_main, (ViewGroup) findViewById (android. R. id. content), true );

Root. addView (temp, params) has been executed internally in this method; the above has been parsed.

We can also see that the difference between inflate3 overload methods is exactly the same as our speculation. I believe that you can also choose the best way for future use. However, from the perspective of ViewGroup and View, we cannot solve this problem if layoutParams is null.

5. analysis from the ViewGroup and View perspectives

If you have some knowledge about custom ViewGroup and custom View, you will certainly not be unfamiliar with the onMeasure method:
What the onMeasure method of ViewGroup does is:

Set the Measurement Mode and measured value for childView.

How to Set it? Is based on LayoutParams.
If the childView width is LayoutParams. MATCH_PARENT, set the mode to MeasureSpec. EXACTLY and calculate the width for childView.
If the width of childView is fixed (that is, greater than 0), set the mode to MeasureSpec. EXACTLY and use lp. width directly as the width of childView.
If the childView width is LayoutParams. WRAP_CONTENT, set the mode to MeasureSpec. AT_MOST.
The height is similar to the width.

OnMeasure method of View:
The main task is to calculate the Desired width and height based on the Measurement Mode and measurement value passed in by ViewGroup:
Generally, this process is as follows:
If the width mode is AT_MOST, calculate the width.
If the width mode is EXACTLY: You can directly use MeasureSpec. getSize (widthMeasureSpec );

For the last part, if it is not clear, it doesn't matter. I will explain it in detail in the future when customizing ViewGroup and custom View.

This process is probably like this. The real drawing process must be more complicated than this. It is to show that if the width and height of the View are set to an accurate value, it must depend on LayoutParams, therefore, our inflate (resId, null) fails to properly handle the width and height.









In Android, LayoutInflater does not reference related resources.

You can check the console prompts. Some errors, such as resource naming errors, may also cause such errors.
Other resource naming errors may cause the resource to be unrecognized, even if the resource already exists in the layout.
 
How does android initialize the value of the text box in the View of LayoutInflater?

Settext ("xxx ");

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.