• 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(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);

    我们查看一下源码:

     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(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

  • 相关阅读:
    COBBLER无人值守安装
    消息头 Content-Type引发的问题:Jmeter请求中postdata不是期望的,响应数据请求参数为null;已经请求没问题,可变量还是为空
    python爬虫-'gbk' codec can't encode character 'xa0' in position 134: illegal multibyte sequence
    正则表达式30分钟入门教程-链接
    linux常见命令学习汇总3-控制语句
    postman循环操作及响应判断-支持文本多变量输入
    linux常见命令学习汇总2-运算符
    linux常见命令学习汇总1
    Jmeter连接数据库方法与问题:Cannot load JDBC driver class 'com.mysql.jdbc.Driver'
    mysql学习笔记-ifnull()函数与nullif()函数
  • 原文地址:https://www.cnblogs.com/cqumonk/p/4706122.html
Copyright © 2020-2023  润新知