LinearLayout的简单分析

来源:互联网 发布:网络婚姻诈骗 编辑:程序博客网 时间:2024/06/05 06:01

LinearLayout的简单分析

本篇通过分析LinearLayout的实现机制来理解该怎么进行自定义组合View

我们可能这样在xml中来使用LinearLayout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="TextView1" />    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="TextView2" />    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="TextView3" /></LinearLayout>

在MainActivity的onCreate中

@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);}

我们直接setContentView,将LinearLayout布局通过LayoutInflater转换成View加入DecorView中,这里我就不详细分析,在6.0代码中,可以查看support/v7/appcompat/src/AppCompatDelegateImpIV7.java:

@Overridepublic void setContentView(int resId) {    ensureSubDecor();    ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);    contentParent.removeAllViews();    LayoutInflater.from(mContext).inflate(resId, contentParent);    mOriginalWindowCallback.onContentChanged();}

在LayoutInflater中的inflate中可以看出将采用pull解析将xml的节点解析成view,加入DecorView中的

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) {            InflateException ex = new InflateException(e.getMessage());            ex.initCause(e);            throw ex;        } catch (Exception 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);        return result;    }

我们查看LinearLayout源码的三个构造函数,发现这三个函数都会调用这个构造方法

 public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {    super(context, attrs, defStyleAttr, defStyleRes);    initLinearLayout();    final TypedArray a = context.obtainStyledAttributes(            attrs, com.android.internal.R.styleable.LinearLayout, defStyleAttr, defStyleRes);    int index = a.getInt(com.android.internal.R.styleable.LinearLayout_orientation, -1);    if (index >= 0) {        setOrientation(index);    }    index = a.getInt(com.android.internal.R.styleable.LinearLayout_gravity, -1);    if (index >= 0) {        setGravity(index);    }    boolean baselineAligned = a.getBoolean(R.styleable.LinearLayout_baselineAligned, true);    if (!baselineAligned) {        setBaselineAligned(baselineAligned);    }    mWeightSum = a.getFloat(R.styleable.LinearLayout_weightSum, -1.0f);    mBaselineAlignedChildIndex =            a.getInt(com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1);    mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false);    setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider));    mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE);    mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0);    a.recycle();}

这个方法是读取自定义属性,在TypeArray中获取自定义属性的值,这个TypeArray封装了自定义属性的值,它的原理实现也是通过pull解析atts.xml中的declare-styleable的属性列表,封装成TypeArray对象

     public TypedArray obtainStyledAttributes(AttributeSet set,            @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {        final int len = attrs.length;        final TypedArray array = TypedArray.obtain(Resources.this, len);        // XXX note that for now we only work with compiled XML files.        // To support generic XML files we will need to manually parse        // out the attributes from the XML file (applying type information        // contained in the resources and such).        final XmlBlock.Parser parser = (XmlBlock.Parser)set;        AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,                parser != null ? parser.mParseState : 0, attrs, array.mData, array.mIndices);        array.mTheme = this;        array.mXml = parser;        if (false) {            int[] data = array.mData;            System.out.println("Attributes:");            String s = "  Attrs:";            int i;            for (i=0; i<set.getAttributeCount(); i++) {                s = s + " " + set.getAttributeName(i);                int id = set.getAttributeNameResource(i);                if (id != 0) {                    s = s + "(0x" + Integer.toHexString(id) + ")";                }                s = s + "=" + set.getAttributeValue(i);            }            System.out.println(s);            s = "  Found:";            TypedValue value = new TypedValue();            for (i=0; i<attrs.length; i++) {                int d = i*AssetManager.STYLE_NUM_ENTRIES;                value.type = data[d+AssetManager.STYLE_TYPE];                value.data = data[d+AssetManager.STYLE_DATA];                value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];                value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];                s = s + " 0x" + Integer.toHexString(attrs[i])                    + "=" + value;            }            System.out.println(s);        }        return array;    }

我们查看LinearLayout自定义属性所在的位置frameworks/base/core/res/res/attrs.xml

 <declare-styleable name="LinearLayout">    <!-- Should the layout be a column or a row?  Use "horizontal"         for a row, "vertical" for a column.  The default is         horizontal. -->    <attr name="orientation" />    <attr name="gravity" />    <!-- When set to false, prevents the layout from aligning its children's         baselines. This attribute is particularly useful when the children         use different values for gravity. The default value is true. -->    <attr name="baselineAligned" format="boolean" />    <!-- When a linear layout is part of another layout that is baseline      aligned, it can specify which of its children to baseline align to      (that is, which child TextView).-->    <attr name="baselineAlignedChildIndex" format="integer" min="0"/>    <!-- Defines the maximum weight sum. If unspecified, the sum is computed         by adding the layout_weight of all of the children. This can be         used for instance to give a single child 50% of the total available         space by giving it a layout_weight of 0.5 and setting the weightSum         to 1.0. -->    <attr name="weightSum" format="float" />    <!-- When set to true, all children with a weight will be considered having         the minimum size of the largest child. If false, all children are         measured normally. -->    <attr name="measureWithLargestChild" format="boolean" />    <!-- Drawable to use as a vertical divider between buttons. -->    <attr name="divider" />    <!-- Setting for which dividers to show. -->    <attr name="showDividers">        <flag name="none" value="0" />        <flag name="beginning" value="1" />        <flag name="middle" value="2" />        <flag name="end" value="4" />    </attr>    <!-- Size of padding on either end of a divider. -->    <attr name="dividerPadding" format="dimension" /></declare-styleable><declare-styleable name="LinearLayout_Layout">    <attr name="layout_width" />    <attr name="layout_height" />    <attr name="layout_weight" format="float" />    <attr name="layout_gravity" /></declare-styleable>

这里定义了LinearLayout的一些属性,如我们常用的orientation,grayvity等,然后我们会在onDraw中根据所定义的orientation来进行绘制

@Overrideprotected void onDraw(Canvas canvas) {    if (mDivider == null) {        return;    }    if (mOrientation == VERTICAL) {        drawDividersVertical(canvas);    } else {        drawDividersHorizontal(canvas);    }}

经过上面LinearLayout的分析,我们能够得到一个自定义view的思路,我们可以通过继承现有控件,自定义declare-styleable属性,重写构造函数获取自定义属性来得到我们的组合自定义view的实现。

0 0