Android LayoutInflater.inflate各個引數作
Layinflater
概述
LayoutInflater這個類相信大家都不陌生,當我們平時需要載入layout檔案來轉換成View的場景都會用到它,其中最常用的有如下兩個載入方法:
View inflate(int resource, ViewGroup root)
View inflate(int resource, ViewGroup root, boolean attachToRoot)
可能一直在使用,卻並未瞭解其中引數真正的奧義所在,讓我們從原始碼中看下這幾個引數究竟代表著什麼。
原始碼分析
先看下兩個引數的inflate,原始碼如下:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) { return inflate(resource, root, root != null); }
可以看到,其實它內部還是呼叫了三個引數的inflate,且只有root不為null時,attachToRoot這個引數才會傳true。那我們可以直接看三個引數的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(); } }
首先獲取資源物件然後將其轉換為XmlResourceParser物件,XmlResourceParser物件可以解析layout.xml檔案中的具體屬性和標籤等資訊,那就進而看inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)
這個方法:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); 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 } 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, inflaterContext, attrs, false); } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, inflaterContext, 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 against its context. rInflateChildren(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) { final InflateException ie = new InflateException(e.getMessage(), e); ie.setStackTrace(EMPTY_STACK_TRACE); throw ie; } catch (Exception e) { final InflateException ie = new InflateException(parser.getPositionDescription() + ": " + e.getMessage(), e); ie.setStackTrace(EMPTY_STACK_TRACE); throw ie; } finally { // Don't retain static reference on context. mConstructorArgs[0] = lastContext; mConstructorArgs[1] = null; Trace.traceEnd(Trace.TRACE_TAG_VIEW); } return result; } }
我們一步步來看,首先是讀取START_TAG
,這個代表著xml檔案裡的根標籤,如果讀不到,說明沒有設定好根部標籤,丟擲No start tag found!
的異常,接著判斷根標籤是否為merge
標籤,如果是的話,attachToRoot
必須為true且root
不能為null,這也就是為什麼如果你要載入的layout檔案中根佈局是merge就必須設定父佈局的原因。
接著會讀取我們的layout檔案,將其中的根佈局讀出來轉換為View物件(即原始碼中的temp),然後判斷root
是否為null,假如root
不為空,則會呼叫root
的layoutparams,並且設定給temp
,如果root
不為空並且attachRoot
也為true時,就會呼叫addView()
,將temp
的layoutparams設定給它自己,然後再將root
作為最終結果傳出去,如果root
為null或者attachRoot
為false,則將temp
返回出去(此時的temp
還是一開始初始化的我們layout檔案的根佈局View)。
通俗一點講就是如下三種情況:
如果root為null或者attachToRoot為false時,則呼叫layout.xml中的根佈局的屬性並且將其作為一個View物件返回。
如果root不為null,但attachToRoot為false時,則先將layout.xml中的根佈局轉換為一個View物件,再呼叫傳進來的root的佈局屬性設定給這個View,然後將它返回。
如果root不為null,且attachToRoot為true時,則先將layout.xml中的根佈局轉換為一個View物件,再將它add給root,最終再把root返回出去。(兩個引數的inflate如果root不為null也是相當於這種情況)
驗證
原始碼分析完了,總得舉個例子驗證一下,先建立一個簡單的自定義View繼承於LinearLayout如下:
/** * Created by YANG on 2019/2/22. */public class ViewGroupTestView extends LinearLayout{ public ViewGroupTestView(Context context) { this(context, null); } public ViewGroupTestView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public ViewGroupTestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); View view = LayoutInflater.from(context).inflate(R.layout.layout_view_group, null); Log.d("Android小Y", view.getClass().getName()); } }
layout_view_group.xml最外層用一個RelativeLayout包裹,如下:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="" android:layout_width="match_parent" android:layout_height="match_parent"></RelativeLayout>
日誌列印如下:
02-16 02:47:34.267 24091-24091/com.example.test.main D/Android小Y: android.widget.RelativeLayout
可以看到為root傳的是null,也就是預設導致attachToRoot也為false,這個時候就使用layout_view_group.xml中的根佈局(即RelativeLayout)返回。
接著我們把引數改一下,改為如下:
View view = LayoutInflater.from(context).inflate(R.layout.layout_view_group, this, false);
可以看到將我們的LinearLayout作為root傳進去,但attachToRoot為false,這個時候列印出來依然型別是RelativeLayout。
如果在此基礎上把attachToRoot設為true:
View view = LayoutInflater.from(context).inflate(R.layout.layout_view_group, this, true);
結果如下:
02-16 03:02:15.630 24634-24634/? D/Android小Y: com.example.test.main.ViewGroupTestView
可以看到這次不是RelativeLayout了,而是我們傳進去的自定義View物件,那按我們剛才的猜想,這個物件的第一個子View是不是應該就是RelativeLayout呢,測試一下:
View view = LayoutInflater.from(context).inflate(R.layout.layout_view_group, this, true); ViewGroupTestView testView = (ViewGroupTestView) view; Log.d("Android小Y", testView.getChildAt(0).getClass().getName());
日誌如下:
02-16 03:07:21.805 24923-24923/com.example.test.main D/Android小Y: android.widget.RelativeLayout
果不其然,我們將第一個子View列印出來,正是RelativeLayout,說明已經被新增進去了。
總結
說了這麼多,總結一下個人對這幾個引數的理解吧。
三個引數時,root的引數是否為空,決定了我們要不要沿用root的佈局屬性,attachToRoot是否為true,決定了是否將我們的layout作為子View新增進去。
兩個引數時,root既決定了是否要沿用root的佈局屬性,也決定了是否要將我們的layout作為子View新增進去。
返回值:一旦root為空或者attachToRoot為false,返回值為xml的根View,其它情況都會返回root
作者:Android小Y
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2334/viewspace-2821942/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Oracle引數檔案 各引數解釋Oracle
- Oracle exp query引數 轉義符的各個作業系統通用解決方法Oracle作業系統
- beego註解路由中各個引數解釋Go路由
- Android中深入理解 LayoutInflater.inflate()Android
- ntpq –p 各引數詳解
- [android]android自動化測試三之設定AVD各項引數Android
- Nodejs獲取url路徑中的各個引數NodeJS
- oracle11g_v$statname各個引數的講解Oracle
- Android 各個版本WebViewAndroidWebView
- 物件,函式作為一個函式的引數物件函式
- Android中ListView.getCount()與ListView.getChildCount()區別和OnScrollListener()各個引數的區別AndroidView
- android:佈局引數,控制元件屬性及各種xml的作用Android控制元件XML
- shell前一個命令結果作為下一個命令引數
- Java 獲取系統各項引數Java
- caffe網路各層引數詳解
- 各平臺影響oracle Process數的引數(轉)Oracle
- 把物件作為引數(轉)物件
- 機器學習(3),opencv4.0中SVM各個引數的意義,設定機器學習OpenCV
- [高頻面試]解釋執行緒池的各個引數含義面試執行緒
- redis配置檔案中各引數詳解Redis
- 12.MyBatis學習--對映檔案_引數處理_單個引數&多個引數&命名引數MyBatis
- 從原始碼分析 Redis 非同步刪除各個引數的具體作用原始碼Redis非同步
- 函式作為引數傳遞函式
- golang 方法作為引數傳遞Golang
- Hadoop作業調優引數Hadoop
- 把介面當作引數傳入
- 各個作業系統的 作業系統日誌作業系統
- 在 Angularjs 中 ui-sref 和 $state.go 如何傳遞單個多個引數和將物件作為引數AngularJSUIGo物件
- 自動生成介面各種逆向組合引數
- [FFmpeg]ffmpeg各類引數說明與使用示例
- js常見演算法(一):陣列去重,打亂陣列,統計陣列各個元素出現的次數, 字串各個字元的出現次數,獲取地址連結的各個引數JS演算法陣列字串字元
- DG學習筆記(6)_LOG_ARCHIVE_DEST_N引數的各個屬性筆記Hive
- AIX作業系統核心引數配置AI作業系統
- Android Jenkins引數化配置AndroidJenkins
- 風控GPS的各種演算法引數演算法
- MVC接收以post形式傳輸的各種引數MVC
- php全面獲取url位址列及各種引數PHP
- 深入理解LayoutInflater.inflate()