Android使用LayoutInflater來進行佈局加載,一般獲取方式有兩種:ide
第一種:佈局
LayoutInflater layoutInflater = LayoutInflater.from(context);
第二種:spa
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
從源碼中能夠看出第一種是第二種的封裝簡化,便於使用:設計
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 }
咱們經過調用inflate方法即可以完成對佈局的加載:code
layoutInflater.inflate(resource, root, true);
LayoutInflater中的inflate方法有若干種重載方式,最終都調用了以下代碼:xml
1 public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { 2 synchronized (mConstructorArgs) { 3 //獲取xml中屬性信息 4 final AttributeSet attrs = Xml.asAttributeSet(parser); 5 Context lastContext = (Context)mConstructorArgs[0]; 6 mConstructorArgs[0] = mContext; 7 View result = root; 8 9 try { 10 // 查找根節點. 11 int type; 12 while ((type = parser.next()) != XmlPullParser.START_TAG && 13 type != XmlPullParser.END_DOCUMENT) { 14 // Empty 15 } 16 17 if (type != XmlPullParser.START_TAG) { 18 throw new InflateException(parser.getPositionDescription() 19 + ": No start tag found!"); 20 } 21 //獲取根節點名稱 22 final String name = parser.getName(); 23 24 if (DEBUG) { 25 System.out.println("**************************"); 26 System.out.println("Creating root view: " 27 + name); 28 System.out.println("**************************"); 29 } 30 //若是是merge標籤,必須保證父節點不爲null且attachToRoot爲true 31 if (TAG_MERGE.equals(name)) { 32 if (root == null || !attachToRoot) { 33 throw new InflateException("<merge /> can be used only with a valid " 34 + "ViewGroup root and attachToRoot=true"); 35 } 36 37 rInflate(parser, root, attrs, false); 38 } else { 39 //表明佈局文件中根節點的view 40 View temp; 41 if (TAG_1995.equals(name)) { 42 temp = new BlinkLayout(mContext, attrs); 43 } else { 44 //利用反射,經過root名稱建立view 45 temp = createViewFromTag(root, name, attrs); 46 } 47 48 ViewGroup.LayoutParams params = null; 49 50 if (root != null) { 51 if (DEBUG) { 52 System.out.println("Creating params from root: " + 53 root); 54 } 55 // Create layout params that match root, if supplied 56 //當提供了父容器時,由父容器根據屬性值建立佈局參數 57 params = root.generateLayoutParams(attrs); 58 if (!attachToRoot) { 59 // Set the layout params for temp if we are not 60 // attaching. (If we are, we use addView, below) 61 //當不把當前view附加到父容器中,則設置獲取到的佈局參數 62 //不然使用下面的addView方法設置 63 temp.setLayoutParams(params); 64 } 65 } 66 67 if (DEBUG) { 68 System.out.println("-----> start inflating children"); 69 } 70 // Inflate all children under temp 71 //遞歸調用此方法加載子佈局 72 rInflate(parser, temp, attrs, true); 73 if (DEBUG) { 74 System.out.println("-----> done inflating children"); 75 } 76 77 // We are supposed to attach all the views we found (int temp) 78 // to root. Do that now. 79 if (root != null && attachToRoot) { 80 root.addView(temp, params); 81 } 82 83 // Decide whether to return the root that was passed in or the 84 // top view found in xml. 85 if (root == null || !attachToRoot) { 86 result = temp; 87 } 88 } 89 90 } catch (XmlPullParserException e) { 91 InflateException ex = new InflateException(e.getMessage()); 92 ex.initCause(e); 93 throw ex; 94 } catch (IOException e) { 95 InflateException ex = new InflateException( 96 parser.getPositionDescription() 97 + ": " + e.getMessage()); 98 ex.initCause(e); 99 throw ex; 100 } finally { 101 // Don't retain static reference on context. 102 mConstructorArgs[0] = lastContext; 103 mConstructorArgs[1] = null; 104 } 105 106 return result; 107 } 108 }
這裏,Android使用了PULL來解析xml佈局文件,並經過反射來建立出當前view:對象
temp = createViewFromTag(root, name, attrs);
咱們查看一下源碼:blog
1 View createViewFromTag(View parent, String name, AttributeSet attrs) { 2 if (name.equals("view")) { 3 name = attrs.getAttributeValue(null, "class"); 4 } 5 6 if (DEBUG) System.out.println("******** Creating view: " + name); 7 8 try { 9 View view; 10 if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs); 11 else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs); 12 else view = null; 13 14 if (view == null && mPrivateFactory != null) { 15 view = mPrivateFactory.onCreateView(parent, name, mContext, attrs); 16 } 17 18 if (view == null) { 19 if (-1 == name.indexOf('.')) { 20 view = onCreateView(parent, name, attrs); 21 } else { 22 view = createView(name, null, attrs); 23 } 24 } 25 26 if (DEBUG) System.out.println("Created view is: " + view); 27 return view; 28 29 } catch (InflateException e) { 30 throw e; 31 32 } catch (ClassNotFoundException e) { 33 InflateException ie = new InflateException(attrs.getPositionDescription() 34 + ": Error inflating class " + name); 35 ie.initCause(e); 36 throw ie; 37 38 } catch (Exception e) { 39 InflateException ie = new InflateException(attrs.getPositionDescription() 40 + ": Error inflating class " + name); 41 ie.initCause(e); 42 throw ie; 43 } 44 }
裏面根據不一樣狀況,調用了onCreateView方法,利用反射來建立view。其中能夠使用指定的factory來建立view,這樣的鉤子設計使得inflate方法變得十分靈活。遞歸
而後調用rInflate(parser, temp, attrs, true)方法來遞歸查找temp中的子view,並添加到上層view中:ip
1 void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs, 2 boolean finishInflate) throws XmlPullParserException, IOException { 3 4 final int depth = parser.getDepth(); 5 int type; 6 7 while (((type = parser.next()) != XmlPullParser.END_TAG || 8 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { 9 10 if (type != XmlPullParser.START_TAG) { 11 continue; 12 } 13 14 final String name = parser.getName(); 15 16 if (TAG_REQUEST_FOCUS.equals(name)) { 17 parseRequestFocus(parser, parent); 18 } else if (TAG_INCLUDE.equals(name)) { 19 if (parser.getDepth() == 0) { 20 throw new InflateException("<include /> cannot be the root element"); 21 } 22 parseInclude(parser, parent, attrs); 23 } else if (TAG_MERGE.equals(name)) { 24 throw new InflateException("<merge /> must be the root element"); 25 } else if (TAG_1995.equals(name)) { 26 final View view = new BlinkLayout(mContext, attrs); 27 final ViewGroup viewGroup = (ViewGroup) parent; 28 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); 29 rInflate(parser, view, attrs, true); 30 viewGroup.addView(view, params); 31 } else { 32 final View view = createViewFromTag(parent, name, attrs); 33 final ViewGroup viewGroup = (ViewGroup) parent; 34 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); 35 rInflate(parser, view, attrs, true); 36 viewGroup.addView(view, params); 37 } 38 } 39 40 if (finishInflate) parent.onFinishInflate(); 41 }
裏面也用到onCreateView方法建立子view,而後將其加入到父view中返回。
經過查看上面的源碼,咱們能夠發現inflate方法中的三個參數int resource, ViewGroup root, boolean attachToRoot的做用以下:
resource指定了要加載的view,root做爲view外面一層的父容器,attachToRoot表示是否將view加入到父容器。
當指定了父容器,而且attachToRoot爲true,則將view加入到父容器中。
若是指定了父容器,卻將attachToRoot設置爲false,那麼只是從父容器中生成了view佈局的參數並設置給view
當未指定父容器時,直接返回view自己。
總結
經過研究LayoutInflater源碼的設計,咱們瞭解到代碼的執行細節的同時,也能夠發現:
LayoutInflater建立view對象時候使用了簡單工廠模式,並經過加入鉤子方法,利用抽象工廠模式讓coder能夠使用自定義的工廠方法來建立view。