LayoutInflater 源码解析

LayoutInflater

LayoutInflater 主要作用是加载以xml 为后缀的布局文件,

  1. 基本用法

    有两种实现方式,如下:

    1
    2
    3
    LayoutInflater layoutInflater = LayoutInflater.from(context);
    LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  2. 加载布局文件

    官方文档中提供四种方法,我们经常使用其中的两种,如下:

    1
    2
    3
    View inflate (int resource,ViewGroup root)
    View inflate (int resource,ViewGroup root,boolean attachToRoot)

    先说一下这几个的参数,resource 是要加载的布局id,root 是指给该布局的外部再嵌套一层父布局,如果不需要直接传null,attachToRoot 是一个布尔值,为 true 表示返回的 View 是 root,也就是根布局,按照 LayoutParams 参数设置的 View 将会被加入到 Root 中;为 false 表示返回的是按照父布局的 LayoutParams 参数设置的 View;

    总结一下:

    1. 如果 root 为 null,attachToRoot 没有任何意义。
    2. 如果 root 不为 null,attachToRoot 为 true,会为加载的布局文件指定一个父布局,即 root。
    3. 如果 root 不为 null,attackToRoot 为 false,则将布局文件最外层的所有 layout 属性进行设置,当该 view 被添加到父 View 中时,这些 layout 属性会自动失效。完全按照父布局的 params 去设置。
    4. 如果不设置 attachToRoot 参数的情况下,如果 root 不为 null,attachToRoot 参数默认为 true。
  3. 源码解析

    inflate 方法的重载最终会走到下面代码中,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
    final Context inflaterContext = mContext;
    final AttributeSet attrs = Xml.asAttributeSet(parser);
    Context lastContext = (Context) mConstructorArgs[0];
    mConstructorArgs[0] = inflaterContext;
    View result = root;
    try {
    // Look for the root node.
    int type;
    while ((type = parser.next()) != XmlPullParser.START_TAG &&
    type != XmlPullParser.END_DOCUMENT) {
    // Empty
    }
    if (type != XmlPullParser.START_TAG) {
    throw new InflateException(parser.getPositionDescription()
    + ": No start tag found!");
    }
    final String name = parser.getName();
    if (DEBUG) {
    System.out.println("**************************");
    System.out.println("Creating root view: "
    + name);
    System.out.println("**************************");
    }
    if (TAG_MERGE.equals(name)) {
    if (root == null || !attachToRoot) {
    throw new InflateException("<merge /> can be used only with a valid "
    + "ViewGroup root and attachToRoot=true");
    }
    rInflate(parser, root, inflaterContext, attrs, false);
    } else {
    // Temp is the root view that was found in the xml
    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
    ViewGroup.LayoutParams params = null;
    if (root != null) {
    if (DEBUG) {
    System.out.println("Creating params from root: " +
    root);
    }
    // Create layout params that match root, if supplied
    params = root.generateLayoutParams(attrs);
    if (!attachToRoot) {
    // Set the layout params for temp if we are not
    // attaching. (If we are, we use addView, below)
    temp.setLayoutParams(params);
    }
    }
    if (DEBUG) {
    System.out.println("-----> start inflating children");
    }
    // Inflate all children under temp against its context.
    rInflateChildren(parser, temp, attrs, true);
    if (DEBUG) {
    System.out.println("-----> done inflating children");
    }
    // We are supposed to attach all the views we found (int temp)
    // to root. Do that now.
    if (root != null && attachToRoot) {
    root.addView(temp, params);
    }
    // Decide whether to return the root that was passed in or the
    // top view found in xml.
    if (root == null || !attachToRoot) {
    result = temp;
    }
    }
    } catch (XmlPullParserException e) {
    final InflateException ie = new InflateException(e.getMessage(), e);
    ie.setStackTrace(EMPTY_STACK_TRACE);
    throw ie;
    } catch (Exception e) {
    final InflateException ie = new InflateException(parser.getPositionDescription()
    + ": " + e.getMessage(), e);
    ie.setStackTrace(EMPTY_STACK_TRACE);
    throw ie;
    } finally {
    // Don't retain static reference on context.
    mConstructorArgs[0] = lastContext;
    mConstructorArgs[1] = null;
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    return result;
    }
    }

    在上述代码中,调用了 createViewFromTag() 方法,它的作用是根据节点名来创建 View 对象 temp。

    当 root 不为 null,attachToRoot 为 false 时,为 temp 设置了 LayoutParams。

    当 root 不为 null,attachToRoot 为 true 时,将 temp 按照 params 添加到 root 中。

    如果 root 为 null 或者 attachToRoot 为 false, 则将 temp 赋值给 result;

    总结一下,

    Inflate (resId , null ) 只创建 temp ,返回 temp

    Inflate (resId , parent, false )创建 temp,然后执行 temp.setLayoutParams(params); 返回temp

    Inflate (resId , parent, true ) 创建 temp,然后执行 root.addView(temp, params); 最后返回root

    回答一下我们开发中遇到的问题,

    Inflate (resId , null ) 不能正确处理宽和高是因为:layout_width,layout_height是相对了父级设置的,必须与父级的LayoutParams一致。而此temp的getLayoutParams为null

    Inflate (resId , parent,false ) 可以正确处理,因为 temp.setLayoutParams(params);这个 params 正是root.generateLayoutParams(attrs);得到的。

    Inflate (resId , parent,true )不仅能够正确的处理,而且已经把 resId 这个 view 加入到了 parent,并且返回的是 parent,和以上两者返回值有绝对的区别,

Tools 属性

我们可以在xml文件中写下面的话:

1
xmlns:tools="http://schemas.android.com/tools"

tools 属性用来在xml 文件中,用来在布局和编译时控制布局文件的属性;在运行时相应的属性消失。

GIT

  1. 配置全局账户和本地账户

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //全局
    git config --global user.name "name"
    git config --global user.email "name.com"
    //本地
    git config user.name "name"
    git config user.email "name.com"
    //取消全局 user.name
    git config --global --unset user.name
    git config --global --unset user.email
  2. 解决账户冲突

    如果只需要修改最近一次提交,使用 git commit —amend 命令

    1
    git commit --amend --author="Author <Author.com>"

扫清知识盲点,继续努力。