Android LayoutInflater源码解析:你真的能正确使用吗?
版權聲明:本文出自汪磊的博客,未經作者允許禁止轉載。
好久沒寫博客了,最近忙著換工作,沒時間寫,工作剛定下來。稍后有時間會寫一下換工作經歷.接下來進入本篇主題,本來沒想寫LayoutInflater的,不過做項目的時候隨手用了一下,運行發現了一些問題,稍微看了下源碼,決定寫篇博客當作記錄一下吧。
一、LayoutInflater實例化方法(簡單提一下)
activity中我們習慣直接調用getLayoutInflater()方法:
1 @NonNull 2 public LayoutInflater getLayoutInflater() { 3 return getWindow().getLayoutInflater(); 4 }實際調用的是PhoneWindow里面的getLayoutInflater()方法:
1 private LayoutInflater mLayoutInflater; 2 3 @Override 4 public LayoutInflater getLayoutInflater() { 5 6 return mLayoutInflater; 7 } 8 9 public PhoneWindow(Context context) { 10 super(context); 11 mLayoutInflater = LayoutInflater.from(context); 12 }看到了吧,實際調用的是LayoutInflater.from(context)方法進行的實例,LayoutInflater的from源碼如下:
1 public static LayoutInflater from(Context context) { 2 LayoutInflater LayoutInflater = 3 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 4 if (LayoutInflater == null) { 5 throw new AssertionError("LayoutInflater not found."); 6 } 7 return LayoutInflater; 8 }很簡單,最本質就是調用context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)獲取系統啟動的時候就為我們創建好的服務。
至于實際中使用哪一種創建根據實際情況選擇就好了,沒什么多說的。
二、LayoutInflater的inflate方法源碼解析(這才是重點)
inflate重載方法有四個,先看其中三個:
?
1 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) { 2 return inflate(resource, root, root != null); 3 }?
1 public View inflate(XmlPullParser parser, @Nullable ViewGroup root) { 2 return inflate(parser, root, root != null); 3 } 1 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { 2 final Resources res = getContext().getResources(); 3 if (DEBUG) { 4 Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" (" 5 + Integer.toHexString(resource) + ")"); 6 } 7 8 final XmlResourceParser parser = res.getLayout(resource); 9 try { 10 return inflate(parser, root, attachToRoot); 11 } finally { 12 parser.close(); 13 } 14 }最終調用的都是inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot),我們著重分析這個方法就可以了。
源碼:
1 /** 2 * Inflate a new view hierarchy from the specified XML node. Throws 3 * {@link InflateException} if there is an error. 4 * <p> 5 * <em><strong>Important</strong></em> For performance 6 * reasons, view inflation relies heavily on pre-processing of XML files 7 * that is done at build time. Therefore, it is not currently possible to 8 * use LayoutInflater with an XmlPullParser over a plain XML file at runtime. 9 * 10 * @param parser XML dom node containing the description of the view 11 * hierarchy. 12 * @param root Optional view to be the parent of the generated hierarchy (if 13 * <em>attachToRoot</em> is true), or else simply an object that 14 * provides a set of LayoutParams values for root of the returned 15 * hierarchy (if <em>attachToRoot</em> is false.) 16 * @param attachToRoot Whether the inflated hierarchy should be attached to 17 * the root parameter? If false, root is only used to create the 18 * correct subclass of LayoutParams for the root view in the XML. 19 * @return The root View of the inflated hierarchy. If root was supplied and 20 * attachToRoot is true, this is root; otherwise it is the root of 21 * the inflated XML file. 22 */ 23 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { 24 synchronized (mConstructorArgs) { 25 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); 26 27 final Context inflaterContext = mContext; 28 final AttributeSet attrs = Xml.asAttributeSet(parser); 29 Context lastContext = (Context) mConstructorArgs[0]; 30 mConstructorArgs[0] = inflaterContext; 31 View result = root; 32 33 try { 34 // Look for the root node. 35 int type; 36 while ((type = parser.next()) != XmlPullParser.START_TAG && 37 type != XmlPullParser.END_DOCUMENT) { 38 // Empty 39 } 40 41 if (type != XmlPullParser.START_TAG) { 42 throw new InflateException(parser.getPositionDescription() 43 + ": No start tag found!"); 44 } 45 46 final String name = parser.getName(); 47 48 if (DEBUG) { 49 System.out.println("**************************"); 50 System.out.println("Creating root view: " 51 + name); 52 System.out.println("**************************"); 53 } 54 55 if (TAG_MERGE.equals(name)) { 56 if (root == null || !attachToRoot) { 57 throw new InflateException("<merge /> can be used only with a valid " 58 + "ViewGroup root and attachToRoot=true"); 59 } 60 61 rInflate(parser, root, inflaterContext, attrs, false); 62 } else { 63 // Temp is the root view that was found in the xml 64 final View temp = createViewFromTag(root, name, inflaterContext, attrs); 65 66 ViewGroup.LayoutParams params = null; 67 68 if (root != null) { 69 if (DEBUG) { 70 System.out.println("Creating params from root: " + 71 root); 72 } 73 // Create layout params that match root, if supplied 74 params = root.generateLayoutParams(attrs); 75 if (!attachToRoot) { 76 // Set the layout params for temp if we are not 77 // attaching. (If we are, we use addView, below) 78 temp.setLayoutParams(params); 79 } 80 } 81 82 if (DEBUG) { 83 System.out.println("-----> start inflating children"); 84 } 85 86 // Inflate all children under temp against its context. 87 rInflateChildren(parser, temp, attrs, true); 88 89 if (DEBUG) { 90 System.out.println("-----> done inflating children"); 91 } 92 93 // We are supposed to attach all the views we found (int temp) 94 // to root. Do that now. 95 if (root != null && attachToRoot) { 96 root.addView(temp, params); 97 } 98 99 // Decide whether to return the root that was passed in or the 100 // top view found in xml. 101 if (root == null || !attachToRoot) { 102 result = temp; 103 } 104 } 105 106 } catch (XmlPullParserException e) { 107 final InflateException ie = new InflateException(e.getMessage(), e); 108 ie.setStackTrace(EMPTY_STACK_TRACE); 109 throw ie; 110 } catch (Exception e) { 111 final InflateException ie = new InflateException(parser.getPositionDescription() 112 + ": " + e.getMessage(), e); 113 ie.setStackTrace(EMPTY_STACK_TRACE); 114 throw ie; 115 } finally { 116 // Don't retain static reference on context. 117 mConstructorArgs[0] = lastContext; 118 mConstructorArgs[1] = null; 119 120 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 121 } 122 123 return result; 124 } 125 }先不要著急上來就讀源碼邏輯,先看看人家寫的注釋,注釋那么長不看浪費了,其實理解這個方法后兩個參數root,attachToRoot這個方法也就理解的差不多了。接下來,是時候展示我英語6級的實力了:
@param root Optional view to be the parent of the generated hierarchy (if<em>attachToRoot</em> is true), or else simply an object that
provides a set of LayoutParams values for root of the returned
hierarchy (if <em>attachToRoot</em> is false.)
翻譯:root是一個可選的view,可以傳入null,如果我們將attachToRoot設置為true,那么root即為生成視圖的父類,如果attachToRoot
設置為false,root僅僅是一個對象,為返回的視圖提供一系列LayoutParams參數。
@param attachToRoot Whether the inflated hierarchy should be attached to
the root parameter? If false, root is only used to create the
correct subclass of LayoutParams for the root view in the XML.
翻譯:大概意思就是attachToRoot可以決定生成的視圖是否掛載到root這個參數上,如果設置為false,root這個參數僅僅用來為XML布局
生成正確的布局參數。
@return The root View of the inflated hierarchy. If root was supplied and
attachToRoot is true, this is root; otherwise it is the root of
the inflated XML file.
翻譯:inflate方法返回值是生成視圖的根布局,如果root參數被提供(傳入的不是null)并且attachToRoot設置為true,則返回值就是
root參數,否則返回值就是XML布局的跟節點視圖。
好了,以上就是大致的翻譯,接下來分析實際源碼;
28行,生成的attrs是XML布局根節點的布局參數集合。
31行,result默認就是我們傳遞進來的root參數,inflate方法最后返回的就是result。
64行,生成xml布局的根節點temp,如下布局:
1 <?xml version="1.0" encoding="utf-8"?> 2 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:background="@android:color/holo_green_light" 5 android:layout_height="wrap_content" > 6 7 <TextView 8 android:id="@+id/mytext" 9 android:layout_width="match_parent" 10 android:layout_height="60dp" 11 android:textColor="@android:color/holo_blue_dark" 12 android:textSize="30sp" 13 android:gravity="center" 14 android:text="ali" /> 15 </FrameLayout>
我們調用inflate方法將這個布局轉化為view的時候,那么temp即為FrameLayout,布局的根節點。
68-80行:如果我們傳遞進來的root不為null,則執行74行邏輯,根據attrs生成xml根布局節點的布局參數params,75行如果我們傳遞進來的
attachToRoot為false,表示xml布局生成的view不掛載到root上則執行78行邏輯,為temp即xml布局根節點設置布局參數params。
95-97行:如果我們傳遞的root不為null,并且參數attachToRoot為true,表示將xml生成的view掛載到root上則執行root.addView(temp, params)邏輯。
101-103行:如果我們傳遞進來的root為null,或者attachToRoot為false(表示我們自己沒有想為xml布局設定父布局或者xml布局生成的view
不想掛載到root上)則result設置為temp,也就是xml布局的根節點。
123行:最后返回result。
三、個人總結
通過上面對參數的翻譯以及源碼的分析,相信你已經對inflate方法有一定了解,root就是我們想為xml布局生成的view指定一個父類,讓其掛載上,root如果不為null,那么就會為xml布局的根節點生成布局參數。至于你到底想不想成為root的孩子,由attachToRoot決定,false表示不想,true表示想。如果你不想那么root還是會盡心為這輩子不能成為其孩子的view生成布局參數并且設置給view,如果想那么調用addView方法加載進來。
另外要說一點在任何我們不負責將View添加進ViewGroup的情況下都應該將attachToRoot設置為false,比如ListView,RecyclerView。
好了本片到此為止,希望讀完本片你能有更深體會,而不是使用出現問題自己瞎搞,參數變來變去,蒙對了還挺高興。
?
轉載于:https://www.cnblogs.com/leipDao/p/8963800.html
總結
以上是生活随笔為你收集整理的Android LayoutInflater源码解析:你真的能正确使用吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 141. Sqrt(x)【牛顿迭代法求平
- 下一篇: 初学css list-style属性