LinearLayout的简单分析

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>

MainActivityonCreate

@Override
protected 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:

@Override
public 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来进行绘制

@Override
protected void onDraw(Canvas canvas) {
    if (mDivider == null) {
        return;
    }

    if (mOrientation == VERTICAL) {
        drawDividersVertical(canvas);
    } else {
        drawDividersHorizontal(canvas);
    }
}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值