你的位置:首页 > 操作系统

[操作系统]LayoutInflater源码解析


Android使用LayoutInflater来进行布局加载,通常获取方式有两种:

第一种:

LayoutInflater layoutInflater = LayoutInflater.from(context); 

第二种:

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方法便可以完成对布局的加载:

layoutInflater.inflate(resource, root, true); 

LayoutInflater中的inflate方法有若干种重载方式,最终都调用了如下代码:

 1 public View inflate(boolean attachToRoot) { 2     synchronized (mConstructorArgs) { 3       //获取 4       final AttributeSet attrs =  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()) !=  13             type !=  14           // Empty 15         } 16          17         if (type !=  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  85           if (root == null || !attachToRoot) { 86             result = temp; 87           } 88         } 89  90       } catch ( 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来解析

temp = createViewFromTag(root, name, attrs);

 

我们查看一下源码:

 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中:

 1 void rInflate(final AttributeSet attrs, 2       boolean finishInflate) throws  3  4     final int depth = parser.getDepth(); 5     int type; 6  7     while (((type = parser.next()) !=  8         parser.getDepth() > depth) && type !=  9 10       if (type != 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。