Android面试题-LayoutInflater源码分析

源码分析相关面试题

Activity相关面试题

Service相关面试题

与XMPP相关面试题

与性能优化相关面试题

与登录相关面试题

与开发相关面试题

与人事相关面试题

<LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android
"
    android:layout_width="match_parent"
    android:layout_height="100dip"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="test" />

</LinearLayout>
public View getView(int position, View convertView, ViewGroup parent) {

    if (convertView == null) {

        convertView = inflate(R.layout.item_lv_test, null);

    }

    return convertView;

}

如果用上面的代码会发现设置100dp是无效的。而如果换成下面的代码就可以了。

public View getView(int position, View convertView, ViewGroup parent) {

    if (convertView == null) {

        convertView = inflate(R.layout.item_lv_test, null, false);

    }

    return convertView;

}

在Android开发中为了inflate一个布局文件,大体有2种方式,如下所示:

  // 1. get a instance of LayoutInflater, then do whatever you want
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    // 2. you're in some View class, then just call View's static inflate method 
    View.inflate(context, R.layout.xxx_xml, someViewGroup/null);

我们来看看这2种方式的具体源码:

  public static View inflate(Context context, int resource, ViewGroup root) {
        LayoutInflater factory = LayoutInflater.from(context);
        return factory.inflate(resource, root);
    }
 public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }    

由源码可知:

1)现在我们看到实质上都是方法1中的做法,View.inflate只是个helper方法而已(少敲几行代码)。那么我们就先来看看LayoutInflater类。关键代码如下:

 public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }

由以上源码可知:

1)实际上调用3个参数的版本,从这里我们可以看出客户端代码

 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

下图可以看到inflate的调用关系:

说的更明白一点就是对 ViewGroup root, boolean attachToRoot这两个参数的讨论。传入ViewGroup root, boolean attachToRoot的值不同,会出现什么结果呢?

先初略解释下这两个参数:

ViewGroup root:指实例的布局所要放入的根视图。
boolean attachToRoot:指是否附加到传入的根视图。

先分析源码:

 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 {
                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 (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);
                        }
                    }
                    // Inflate all children under temp against its context.
                    rInflateChildren(parser, temp, attrs, true);


                    // 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;
                    }
                }

            } finally {
                // Don't retain static reference on context.
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;
            }

            Trace.traceEnd(Trace.TRACE_TAG_VIEW);

            return result;
        }

源码可知:

1)此方法最后的返回值,初始化为传入的root

 View result = root; 

2)能走到这里,说明type是START_TAG 或 END_DOCUMENT,如果一开始就是END_DOCUMENT,那说明xml文件有问题.

if (type != XmlPullParser.START_TAG)

3) 获得当前start tag的name,能到这里,那type一定是START_TAG,也就是xml文件里的root

final String name = parser.getName(); 

4)判断当前的tag是否是merge标签,root必须非空且attachToRoot为true,否则抛异常结束。

if (TAG_MERGE.equals(name))

5) 递归的inflate

rInflate(parser, root, inflaterContext, attrs, false); 

6)动态载入resource

final View temp = createViewFromTag(root, name, attrs, false)

7)创建ViewGroup的LayoutParams

ViewGroup.LayoutParams params = null;
params = root.generateLayoutParams(attrs);

8)当root不为空,attachToRoot为false时,为temp设置layout属性,当该view以后被添加到父view当中时,这些layout属性会自动生效

if (root != null) {
    params = root.generateLayoutParams(attrs);
    if (!attachToRoot) {
             temp.setLayoutParams(params);
    }
}

9)递归inflate剩下的所有children

  rInflateChildren(parser, temp, attrs, true);

 至此我们已经将LayoutInflater.inflate的关键代码分析完毕了。

猜你喜欢

转载自blog.csdn.net/mwq384807683/article/details/72654768