獲取LayoutInflater對象的方法和inflate方法的一些參數問題


一、獲取LayoutInflater的三種方法

1、

LayoutInflater layoutInflater = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

2、

LayoutInflater layoutInflater = LayoutInflater.from(MainActivity.this);

3、

LayoutInflater layoutInflater = MainActivity.this.getLayoutInflater();

其實查看它們的源碼就會發現,后兩種方法最終也還是調用第一種方法的context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)。不過查看第三種方法的源碼要稍微繞一下,因為activity.getLayoutInflater(),其實是調用Window類的getLayoutInflater(),而這個是抽象類。根據這個類的注釋,可以查看它的子類PhoneWindow,而這個是內部類,用開發工具是找不到這個類的,要手動去找。可以從sdk的文件目錄下找,例如,我的是“D:\Android\Sdk\sources\android-23\com\android\internal\policy”。也可以直接在sdk目錄下,搜索該類就行了。

其實View有一個靜態的inflate方法,連LayoutInflater對象都幫你內部創建了。其方法源碼如下:

    public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
        LayoutInflater factory = LayoutInflater.from(context);
        return factory.inflate(resource, root);
    }

 

 二、inflate方法的一些參數問題

以前是困惑於inflate方法的作用。現在是困惑於什么時候才傳入ViewGroup對象和attachToRoot的取值。所以分析了一下源碼。

當寫ListView的Adapter時,總要在getView方法里調用inflate方法,例如:

convertView = LayoutInflater.from(mContext).inflate(R.layout.push_to_refresh_header, parent, false);

這個inflate方法的源碼是:

    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

這個時候傳入的ViewGroup對象不為nullt,同時設置boolean attachToRoot為false。

 

而在動態添加布局的時候,我學到的是下面這種寫法:

View refreshView = layoutInflater.inflate(R.layout.push_to_refresh_header, null);
linearLayout.addView(refreshView);

這個inflate方法的源碼是:

    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }

這個時候傳入的ViewGroup對象為null。

 

可以看到,當調用兩個參數的inflate方法時,它會以ViewGroup對象不等於空的判斷值為參數attachToRoot的值,並和之前的參數傳到另一個帶有三個參數的inflate方法。

最終都會調用下面這個inflate方法。這里我們只需關注ViewGroup root、boolean attachToRoot和返回值result就可以了,所以我把其它省略了。

  1     public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
  2         synchronized (mConstructorArgs) {
         ......  
 9
View result = root; 10 11 try {            ......
32
33 if (TAG_MERGE.equals(name)) {               ......
40
} else { 41 // Temp is the root view that was found in the xml 42 final View temp = createViewFromTag(root, name, inflaterContext, attrs); 43 44 ViewGroup.LayoutParams params = null; 45 46 if (root != null) {                 ......
51
// Create layout params that match root, if supplied 52 params = root.generateLayoutParams(attrs); 53 if (!attachToRoot) { 54 // Set the layout params for temp if we are not 55 // attaching. (If we are, we use addView, below) 56 temp.setLayoutParams(params); 57 } 58 }               ......
70
71 // We are supposed to attach all the views we found (int temp) 72 // to root. Do that now. 73 if (root != null && attachToRoot) { 74 root.addView(temp, params); 75 } 76 77 // Decide whether to return the root that was passed in or the 78 // top view found in xml. 79 if (root == null || !attachToRoot) { 80 result = temp; 81 } 82 } 83 84 }
         ......
102 return result; 103 } 104 }

在第9行可以看到返回值result的初始值是root。

然后整個方法就只有第79行的if語句會修改result。而條件就是root為null,或者attachToRoot為假。而這個temp在第42行可以找到初值。其實上面的注釋已經告訴我們,temp是我們傳入的布局文件中的根視圖。

在Adapter的getView方法里,雖然傳入的root不為null,但attachToRoot為false,所以返回值就是我們傳入的布局文件中的根視圖,用來為布局中的控件初始化和修改。而上面那種動態添加布局的方式,傳入的root為null,也是如此。

那為什么getView方法不直接傳進null呢?這個我沒能從源碼上找到為什么,只知道,當傳進ViewGroup對象為null時,運行會報錯,說Adapter為null,就是Adapter對象沒有初始化。那如果傳進了root,但令attachToRoot為true呢?也是會運行報錯。

先看看第73行的if語句。它表示的是root不為null,同時attachToRoot為true時,就會把temp和它的布局參數添加到root中。

所以在上面那種情況中,root其實就是ListView,而在ListView的父類AdapterView中,其方法的注釋聲明了是不支持addView方法的,一旦調用就會報錯。

而我上面動態添加布局的那種寫法,也就可以替換成:

View refreshView = layoutInflater.inflate(R.layout.push_to_refresh_header, linearLayout);

把父布局的根視圖傳進去就可以了,它會自動生成attachToRoot的值為true。然后再把要添加的布局中的根視圖添加到root中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM