Android LayoutInflater.inflate的使用及源碼分析

來源:互聯網
上載者:User

標籤:部落格   whether   ade   and   mat   運行   print   ...   初學者   

歡迎轉載,轉載請標明出處:
http://blog.csdn.net/johnny901114/article/details/61913656
本文出自:【餘志強的部落格】

在實際開發中我們常常需要inflate要給布局然後添加到某個版面配置容器裡面去, 要把xml布局檔案轉成一個View對象 需要使用LayoutInflater.inflate方法. 在開發中常常使用如下幾種方式:

inflater.inflate(layoutId, null);inflater.inflate(layoutId, root,false);inflater.inflate(layoutId, root,true);

特別是初學者搞不靈清這種方式有什麼區別, 下面就一一來看看他們到底有什麼不同.

inflater.inflate(layoutId, null)

現在我有一個MainActivity, 我想往這個布局裡add一個布局, 該布局如下所示:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_marginLeft="20dp"    android:background="#50000000"    android:orientation="vertical">    <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="inflate01"        android:textAllCaps="false"        android:textSize="20sp" /></LinearLayout>

該布局非常簡單, 外層是一個LinearLayout, 背景顏色為#50000000, 大小都是wrap_content, 距左20dp.

然後在MainActivity執行添加操作:

LayoutInflater inflater = LayoutInflater.from(this);View view = inflater.inflate(R.layout.inflater01, null);root.addView(view);

添加View後, 發現並不是我們想要的, 如所示:

從可以看出, 被添加的View, 它的寬度並不是wrap_content而是佔滿了螢幕, 還有設定的margin_left也沒有效果. 也就是受我們設定的這些都失效了.

那隻有看看源碼一探究竟了.

inflater.inflate(R.layout.inflater01, null) 方法裡調用了inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot), 在該方法又調用了 inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) 這個方法是最核心的方法:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {    synchronized (mConstructorArgs) {        final Context inflaterContext = mContext;        final AttributeSet attrs = Xml.asAttributeSet(parser);        Context lastContext = (Context) mConstructorArgs[0];        mConstructorArgs[0] = inflaterContext;        View result = root;        try {            // Look for the root node.            int type;            while ((type = parser.next()) != XmlPullParser.START_TAG &&                    type != XmlPullParser.END_DOCUMENT) {                // Empty            }            final String name = parser.getName();            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 {                // Temp is the root view that was found in the xml                //把xml裡的相關屬性轉成View對象                final View temp = createViewFromTag(root, name, inflaterContext, attrs);                ViewGroup.LayoutParams params = null;                //如果參數root不為空白                if (root != null) {                    // Create layout params that match root, if supplied                    //根據在XML檔案中的設定,產生LayoutParamsView的LayoutParameter用來適應root布局                    //root布局的根節點是哪個容器(如LinearLayout)那這個params就是哪個容器布局的params(LinearLayout.LayoutParams)                    params = root.generateLayoutParams(attrs);                    //如果不添加到跟布局, 則設定剛剛產生的params                    if (!attachToRoot) {                        // Set the layout params for temp if we are not                        // attaching. (If we are, we use addView, below)                        temp.setLayoutParams(params);                    }                }                // Inflate all children under temp against its context.                //初始化View所有的子控制項                rInflateChildren(parser, temp, attrs, true);                // We are supposed to attach all the views we found (int temp)                // to root. Do that now.                if (root != null && attachToRoot) {                    //把View添加到root布局中                    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) {            //igore code...        } catch (Exception e) {            //igore code...        } finally {            //igore code...        }        return result;    }}

上面的代碼比較好理解, 主要的地方都加了注釋.

從上面的代碼來看 inflater.inflate(layoutId, null) 方法, 因為root我們傳了null,所以 attachToRoot=false.

根據源碼 inflater.inflate(layoutId, null) 方法執行主要流程如下:

1, 把xml裡的相關屬性轉成View對象

final View temp = createViewFromTag(root, name, inflaterContext, attrs);

2, 最後把 View temp返回了.

所以這個View是沒有 LayoutParams 對象的.

不信的話可以列印一下就知道了:

View view = inflater.inflate(R.layout.inflater01, null);ViewGroup.LayoutParams layoutParams = view.getLayoutParams();if (layoutParams == null) {    Log.e("MainActivity", "layoutParams is null");} else {    Log.e("MainActivity", "layoutParams width:height" + layoutParams.width + ":" + layoutParams.height);}

毫無疑問輸出null.

一個View沒有 LayoutParams 是沒辦法展示到螢幕上的.

現在只有看看 root.addView(view) 方法了:

public void addView(View child) {    addView(child, -1);}public void addView(View child, int index) {    if (child == null) {        throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");    }    LayoutParams params = child.getLayoutParams();    if (params == null) {        params = generateDefaultLayoutParams();        if (params == null) {            throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");        }    }    addView(child, index, params);}

從上面可以看出, 如果 child 的 LayoutParams為null, root會產生一個預設的 LayoutParams

所以看看 generateDefaultLayoutParams() 方法是什麼樣的? 因為我們這個root是LinearLayout, 所以要到 LinearLayout 的 generateDefaultLayoutParams() 裡去看,而不是ViewGroup :

@Overrideprotected LayoutParams generateDefaultLayoutParams() {    if (mOrientation == HORIZONTAL) {        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);    } else if (mOrientation == VERTICAL) {        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);    }    return null;}

我們的root是LinearLayout方向為 VERTICAL 所以 LayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 這就解釋了我們是上面啟動並執行效果了: 明明設定的寬和高都是 WRAP_CONTENT, 最後變成了寬度為 MATCH_PARENT, 高度為WRAP_CONTENT.

viewGroup.addView(view, LayoutParams); 相當於如下兩行代碼 :view.setLayoutParams(LayoutParams);viewGroup.addView(view)

有人可能會問 下面三個屬性失效了,那 android:background="#50000000"又起作用呢?

android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="20dp"

因為layout_width,layout_height,layout_marginLeft,layout_marginLeft都屬於LayoutParams, 但是background屬性不屬於此範圍, 同時所有的標籤屬性都會存在AttributeSet對象裡.

inflater.inflate(layoutId, root,false);

在MainActivity的添加View的方法改成如下形式 :

/** * inflater.inflate(layoutId, root, false); */private void add02() {    View view = inflater.inflate(R.layout.inflater01, root, false);    ViewGroup.LayoutParams layoutParams = view.getLayoutParams();    if (layoutParams == null) {        Log.e("MainActivity", "layoutParams is null");    } else {        Log.e("MainActivity", "layoutParams width:height" + layoutParams.width + ":" + layoutParams.height);    }    root.addView(view);}

布局檔案還是那個布局檔案, 不同的只是把inflate方法的參數該了下:

inflater.inflate(R.layout.inflater01, root, false);

效果如所示 :

這個才是我們要的效果, 寬高和左邊距都是我們要的效果.

根據源碼 inflater.inflate(R.layout.inflater01, root, false);方法執行流程流程如下:

1, 把xml裡的相關屬性轉成View對象

final View temp = createViewFromTag(root, name, inflaterContext, attrs);

2, 根據在XML檔案中的設定,產生LayoutParamsView的LayoutParameter用來適應root布局, 並且設定給View

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);}

2, 最後把 View temp 返回了.

所以, 最終呈現出來的是符合我們的預期的.

inflater.inflate(layoutId, root,true);

inflater.inflate(layoutId, root,true);inflater.inflate(layoutId, root,false); 唯一的區別就是 inflater.inflate(layoutId, root,true); 會自動把View添加到root裡, 不用我們來add進去.

/** * inflater.inflate(layoutId, root, true); */private void add03() {    View view = inflater.inflate(R.layout.inflater01, root, true);    ViewGroup.LayoutParams layoutParams = view.getLayoutParams();    if (layoutParams == null) {        Log.e("MainActivity", "layoutParams is null");    } else {        Log.e("MainActivity", "layoutParams width:height" + layoutParams.width + ":" + layoutParams.height);    }}

Android LayoutInflater.inflate的使用及源碼分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.