LayoutInflater 分析

来源:互联网 发布:python中syntax error 编辑:程序博客网 时间:2024/05/17 08:53

1,获取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;    }

2,LayoutInflater.inflate()方法参数

    public View inflate(int resource, ViewGroup root, boolean attachToRoot) {        final Resources res = getContext().getResources();        if (DEBUG) {            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("                    + Integer.toHexString(resource) + ")");        }        final XmlResourceParser parser = res.getLayout(resource);        try {            return inflate(parser, root, attachToRoot);        } finally {            parser.close();        }    }
无论调用的是inflate的哪一种重载方法,最后调用的都是inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot),
    public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {        synchronized (mConstructorArgs) {            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");            final AttributeSet attrs = Xml.asAttributeSet(parser);            Context lastContext = (Context)mConstructorArgs[0];            mConstructorArgs[0] = mContext;            //定义返回值,初始化为传入的形参root            View result = root;            try {                // Look for the root node.                int type;                while ((type = parser.next()) != XmlPullParser.START_TAG &&                        type != XmlPullParser.END_DOCUMENT) {                    // Empty                }                //如果一开始就是END_DOCUMENT,那说明xml文件有问题                if (type != XmlPullParser.START_TAG) {                    throw new InflateException(parser.getPositionDescription()                            + ": No start tag found!");                }                //有了上面判断说明这里type一定是START_TAG,也就是xml文件里的root node                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)) {                //处理merge tag的情况(merge,你懂的,APP的xml性能优化)                    //root必须非空且attachToRoot为true,否则抛异常结束(APP使用merge时要注意的地方,                    //因为merge的xml并不代表某个具体的view,只是将它包起来的其他xml的内容加到某个上层                    //ViewGroup中。)                    if (root == null || !attachToRoot) {                        throw new InflateException("<merge /> can be used only with a valid "                                + "ViewGroup root and attachToRoot=true");                    }                    //递归inflate方法调运                    rInflate(parser, root, attrs, false, false);                } else {                    // Temp is the root view that was found in the xml                    //xml文件中的root view,根据tag节点创建view对象                    final View temp = createViewFromTag(root, name, attrs, false);                    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                        //根据root生成合适的LayoutParams实例                        params = root.generateLayoutParams(attrs);                        if (!attachToRoot) {                            // Set the layout params for temp if we are not                            // attaching. (If we are, we use addView, below)                            //如果attachToRoot=false就调用view的setLayoutParams方法                            temp.setLayoutParams(params);                        }                    }                    if (DEBUG) {                        System.out.println("-----> start inflating children");                    }                    // Inflate all children under temp                    //递归inflate剩下的children                    rInflate(parser, temp, attrs, true, 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非空且attachToRoot=true则将xml文件的root view加到形参提供的root里                        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) {                        //返回xml里解析的root view                        result = temp;                    }                }            } catch (XmlPullParserException e) {                InflateException ex = new InflateException(e.getMessage());                ex.initCause(e);                throw ex;            } catch (IOException e) {                InflateException ex = new InflateException(                        parser.getPositionDescription()                        + ": " + e.getMessage());                ex.initCause(e);                throw ex;            } finally {                // Don't retain static reference on context.                mConstructorArgs[0] = lastContext;                mConstructorArgs[1] = null;            }            Trace.traceEnd(Trace.TRACE_TAG_VIEW);            //返回参数root或xml文件里的root view            return result;        }    }

当我们infalte布局之后会调用View类的onFinishInflate回调(和setContentView的回调
onContentChanged
类似),所以,可以看见,当我们自定义View时在构造函数inflate一个xml后可以实现onFinishInflate这个方法一些自定义的逻辑。
    /**     * Finalize inflating a view from XML.  This is called as the last phase     * of inflation, after all child views have been added.     *     * <p>Even if the subclass overrides onFinishInflate, they should always be     * sure to call the super method, so that we get called.     */    protected void onFinishInflate() {    }



根据源码再来分析inflate方法各参数的含义:
  • inflate(xmlId, null); 只创建temp的View,然后直接返回temp,xml中最外层布局的 宽高 失效。
  • inflate(xmlId, parent); 创建temp的View,然后执行root.addView(temp, params);最后返回root。xml中最外层布局的 宽高 有效。
  • inflate(xmlId, parent, false); 创建temp的View,然后执行temp.setLayoutParams(params);然后再返回temp。
  • inflate(xmlId, parent, true); 创建temp的View,然后执行root.addView(temp, params);最后返回root。
  • inflate(xmlId, null, false); 只创建temp的View,然后直接返回temp。
  • inflate(xmlId, null, true); 只创建temp的View,然后直接返回temp。
接下来重点来了:
我们通常在xml布局文件中,使用layout_width和layout_height来设置View的大小,但其实这不是view的真是大小,实际设置的是view在ViewGroup布局中的大小,所以
反过来看命名,是layout_width,而不是width。
但是,问题来了,我们在setContentView的时候,也是通过inflate注入布局的,那时候xml最外层的布局指定大小都是有效的啊,那是为什么呢?
因为,xml文件布局是放在id为content的Fragment中的,所以layout_width和layout_height才会生效。

3,inflate(xmlId, parent, true); 创建temp的View,然后执行root.addView(temp, params);最后返回root。layout_width和layout_height的真实含义

  例子:listView中的item通过inflate的方式,注入布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <ListView        android:id="@+id/listview"        android:dividerHeight="5dp"        android:layout_width="match_parent"        android:layout_height="match_parent"></ListView></LinearLayout>

textview_layout.xml文件:
<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="100dp"    android:layout_height="40dp"    android:text="Text Test"    android:background="#ffa0a00c"/>
textview_layout_parent.xml文件:
<?xml version="1.0" encoding="utf-8"?><LinearLayout    android:layout_height="wrap_content"    android:layout_width="wrap_content"    xmlns:android="http://schemas.android.com/apk/res/android">    <TextView xmlns:android="http://schemas.android.com/apk/res/android"        android:layout_width="100dp"        android:layout_height="40dp"        android:text="Text Test"        android:background="#ffa0a00c"/></LinearLayout>

ListView的自定义Adapter文件:
public class InflateAdapter extends BaseAdapter {    private LayoutInflater mInflater = null;    public InflateAdapter(Context context) {        mInflater = LayoutInflater.from(context);    }    @Override    public int getCount() {        return 8;    }    @Override    public Object getItem(int position) {        return null;    }    @Override    public long getItemId(int position) {        return 0;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        //说明:这里是测试inflate方法参数代码,不再考虑性能优化等TAG处理        return getXmlToView(convertView, position, parent);    }    private View getXmlToView(View convertView, int position, ViewGroup parent) {        View[] viewList = {                mInflater.inflate(R.layout.textview_layout, null),//                mInflater.inflate(R.layout.textview_layout, parent),                mInflater.inflate(R.layout.textview_layout, parent, false),//                mInflater.inflate(R.layout.textview_layout, parent, true),                mInflater.inflate(R.layout.textview_layout, null, true),                mInflater.inflate(R.layout.textview_layout, null, false),                mInflater.inflate(R.layout.textview_layout_parent, null),//                mInflater.inflate(R.layout.textview_layout_parent, parent),                mInflater.inflate(R.layout.textview_layout_parent, parent, false),//                mInflater.inflate(R.layout.textview_layout_parent, parent, true),                mInflater.inflate(R.layout.textview_layout_parent, null, true),                mInflater.inflate(R.layout.textview_layout_parent, null, false),        };        convertView = viewList[position];        return convertView;    }}
效果图:
  • mInflater.inflate(R.layout.textview_layout, null)不能正确处理我们设置的宽和高是因为layout_width,layout_height是相对了父级设置的,而此temp的getLayoutParams为null。
  • mInflater.inflate(R.layout.textview_layout, parent)能正确显示我们设置的宽高是因为我们的View在设置setLayoutParams时params = root.generateLayoutParams(attrs)不为空。Inflate(resId , parent,false ) 可以正确处理,因为temp.setLayoutParams(params);这个params正是root.generateLayoutParams(attrs);得到的
  • mInflater.inflate(R.layout.textview_layout, null, true)与mInflater.inflate(R.layout.textview_layout, null, false)不能正确处理我们设置的宽和高是因为layout_width,layout_height是相对了父级设置的,而此temp的getLayoutParams为null
  • textview_layout_parent.xml作为item可以正确显示的原因是因为TextView具备上级ViewGroup,上级ViewGroup的layout_width,layout_height会失效,当前的TextView会有效而已。

4,布局优化技巧


http://blog.csdn.net/yanbober/article/details/45970721

0 0