LayoutInflater源碼解析

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

相關文章
相關標籤/搜索