Android LayoutParams 学习一

1 如何添加View或者ViewGroup到ViewGroup中

两种方式可以添加View或者ViewGroup到ViewGroup中,一种是xml布局文件中,一种是动态代码添加。
两个View在RelativeLayout中一左一右的布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@color/black"
    android:layout_alignParentLeft="true"
    android:text="ParameterDemo1" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"
        android:layout_alignParentRight="true"
        android:text="ParameterDemo2" />
</RelativeLayout>

动态代码添加

RelativeLayout layout = new RelativeLayout(this);
TextView textView1 = new TextView(this);
TextView textView2 = new TextView(this);
textView1.setText("ParameterDemo1");
textView2.setText("ParameterDemo2");
RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
RelativeLayout.LayoutParams params2 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params1.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
params2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
textView1.setLayoutParams(params1); 
textView2.setLayoutParams(params2);
layout.addView(textView1);
layout.addView(textView2);

动态代码添加和xml添加view,都用到了alignParentLeft,alignParentRight,WRAP_CONTENT等属性,只是表达方式不同它们意义是完全相同的。
我们知道通过xml之所以可以定义布局文件,其实是Android系统帮我们解析了xml文件,然后生成对应的对象。如果自己要解析xml文件可以用LayoutInflater的inflate()方法。

/**
 * 从xml 文件生成view 
 * @param resource xml资源文件
 * @param root 如果attachToRoot 为true则 root作为xml的parent,否则root可以为生成的view提供LayoutParams 
 * @param attachToRoot  是否添加到root

 */
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();
    }
}

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
        final Context inflaterContext = mContext;
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        Context lastContext = (Context) mConstructorArgs[0];
        mConstructorArgs[0] = inflaterContext;
        View result = root;

。。。。。。。。。。。。。。。。。。。。。。。。

                ViewGroup.LayoutParams params = null;

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

                // Inflate all children under temp against its context.
                rInflateChildren(parser, temp, attrs, true);

                 if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }

              if (root == null || !attachToRoot) {
                    result = temp;
                }
            }

               return result;
    }
}

内部利用root.generateLayoutParams生成layoutParams。

LayoutParams作用于View,ViewGroup告诉它们的Parents他们想怎么排布(类似宽,高,偏移,比例等),所以LayoutParams是设置给ViewGroup内部的View或ViewGroup,但最终由父ViewGroup使用,简单说LayoutParams就是View,ViewGroup在其父布局内部的位置信息。

动态添加view,设置LayoutParams的方式:
在这里插入图片描述

  • 第一种,第二种相同:
    textView2.setLayoutParams(params2);
    layout.addView(textView1);

  • 第三种:
    public void addView(View child, LayoutParams params) {
    addView(child, -1, params);
    }

  • 第四种,第五种:
    public void addView(View child, int width, int height) {
    final LayoutParams params = generateDefaultLayoutParams();
    params.width = width;
    params.height = height;
    addView(child, -1, params);
    }

最终都会调用:

public void addView(View child, int index, LayoutParams params) {
    if (DBG) {
        System.out.println(this + " addView");
    }

    if (child == null) {
        throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
    }
    requestLayout();
    invalidate(true);
    addViewInner(child, index, params, false);
}

所以最终都用到了LayoutParams。

2 LayoutParams

ViewGroup内的LayoutParams是所有ViewGroup的基类,layout_width,layout_height就定义在内部 FILL_PARENT ,MATCH_PARENT, WRAP_CONTENT,width,height等也都定义在内部。
几乎所有的系统提供的view都支持margin属性,LayoutParams是不支持margin的所以一般需要继承MarginLayoutParams。
public static class MarginLayoutParams extends ViewGroup.LayoutParams ;

重要的函数generateLayoutParams()函数:
如何让ViewGroup内部的View或者ViewGroup拥有parent ViewGroup希望内部子view拥有的属性,需要实现的generateLayoutParams方法。generateLayoutParams方法会生成内部子view需要的layoutParams类,这样内部子View就会使用generateLayoutParams生成的LayoutParams作为他们LayoutParams的类型,parent ViewGroup就可以从view的LayoutParams获取想要的属性。

3 LinearLayout的LayoutParams

LinearLayout内部的子View设置LayoutParams时必须使用LinearLayout.LayoutParams,我们来看一下LinearLayout.LayoutParams支持什么属性。
generateLayoutParams(attrs)

@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new LinearLayout.LayoutParams(getContext(), attrs);
}

**LinearLayout.LayoutParams 继承了MarginLayoutParams **

public static class LayoutParams extends ViewGroup.MarginLayoutParams {
    /**
     * Indicates how much of the extra space in the LinearLayout will be
     * allocated to the view associated with these LayoutParams. Specify
     * 0 if the view should not be stretched. Otherwise the extra pixels
     * will be pro-rated among all views whose weight is greater than 0.
     */
    @ViewDebug.ExportedProperty(category = "layout")
    public float weight;

    /**
     * Gravity for the view associated with these LayoutParams.
     *
     * @see android.view.Gravity
     */
    @ViewDebug.ExportedProperty(category = "layout", mapping = {
        @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
        @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
        @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
        @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
        @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
        @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
        @ViewDebug.IntToString(from = Gravity.START,             to = "START"),
        @ViewDebug.IntToString(from = Gravity.END,               to = "END"),
        @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
        @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
        @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
        @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
        @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
        @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
    })
    public int gravity = -1;

    /**
     * {@inheritDoc}
     */
    public LayoutParams(Context c, AttributeSet attrs) {
        super(c, attrs);
        TypedArray a =
                c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);

        weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
        gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);

        a.recycle();
    }

    /**
     * {@inheritDoc}
     */
    public LayoutParams(int width, int height) {
        super(width, height);
        weight = 0;
    }

    /**
     * Creates a new set of layout parameters with the specified width, height
     * and weight.
     *
     * @param width the width, either {@link #MATCH_PARENT},
     *        {@link #WRAP_CONTENT} or a fixed size in pixels
     * @param height the height, either {@link #MATCH_PARENT},
     *        {@link #WRAP_CONTENT} or a fixed size in pixels
     * @param weight the weight
     */
    public LayoutParams(int width, int height, float weight) {
        super(width, height);
        this.weight = weight;
    }

    /**
     * {@inheritDoc}
     */
    public LayoutParams(ViewGroup.LayoutParams p) {
        super(p);
    }

    /**
     * {@inheritDoc}
     */
    public LayoutParams(ViewGroup.MarginLayoutParams source) {
        super(source);
    }

    /**
     * Copy constructor. Clones the width, height, margin values, weight,
     * and gravity of the source.
     *
     * @param source The layout params to copy from.
     */
    public LayoutParams(LayoutParams source) {
        super(source);

        this.weight = source.weight;
        this.gravity = source.gravity;
    }

    @Override
    public String debug(String output) {
        return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) +
                ", height=" + sizeToString(height) + " weight=" + weight +  "}";
    }

    /** @hide */
    @Override
    protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
        super.encodeProperties(encoder);

        encoder.addProperty("layout:weight", weight);
        encoder.addProperty("layout:gravity", gravity);
    }
}

LinearLayout.LayoutParams 继承MarginLayoutParams 支持了margin同时还添加了
gravity ,weight属性。

onMeasure时如何获取设置的LayoutParams:

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
    mTotalLength = 0;
    int maxWidth = 0;
    int childState = 0;
    int alternativeMaxWidth = 0;
    int weightedMaxWidth = 0;
    boolean allFillParent = true;
    float totalWeight = 0;

    final int count = getVirtualChildCount();

    final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    boolean matchWidth = false;
    boolean skippedMeasure = false;

    final int baselineChildIndex = mBaselineAlignedChildIndex;
    final boolean useLargestChild = mUseLargestChild;

    int largestChildHeight = Integer.MIN_VALUE;
    int consumedExcessSpace = 0;

    int nonSkippedChildCount = 0;

    // See how tall everyone is. Also remember max width.
    for (int i = 0; i < count; ++i) {
        final View child = getVirtualChildAt(i);
        if (child == null) {
            mTotalLength += measureNullChild(i);
            continue;
        }

        if (child.getVisibility() == View.GONE) {
           i += getChildrenSkipCount(child, i);
           continue;
        }

        nonSkippedChildCount++;
        if (hasDividerBeforeChildAt(i)) {
            mTotalLength += mDividerHeight;
        }

	//获取LayoutParams 读取设置的属性,进行测量
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();

//获取weight
        totalWeight += lp.weight;
        //获取宽高
final boolean useExcessSpace = lp.height == 0 && lp.weight > 0;
            if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) {
                // Optimization: don't bother measuring children who are only
                // laid out using excess space. These views will get measured
                // later if we have space to distribute.
                final int totalLength = mTotalLength;
                //获取margin属性
                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
                skippedMeasure = true;
            } else {
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值