目录
- 第一层理解
- 第二层理解
- 第三层理解
这一期我们来讲一讲LayoutParams这个玩意儿。Android入门的第一行代码就牵扯到这个东西,然而,你真的理解够了吗?
第一层理解
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
tools:context=".MainActivity"> | |
</RelativeLayout> |
layout_width和layout_height这个是不是最开始学的时候,就要搞清楚的基础知识,match_parent代表填充屏幕,wrap_content代表包裹内容。这些其实是系统控件定义的属性,通过TypedArray进行解析。
第二层理解
val lp = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) | |
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT) | |
lp.addRule(RelativeLayout.CENTER_VERTICAL) | |
lp.addRule(RelativeLayout.BELOW, viewId) | |
lp.setMargins(, 20, 10, 20) |
使用代码动态布局的时候设置LayoutParams。
第三层理解
好了,知识是在不断打破旧的认识中进步的,第一层实际还没到LayoutParams,还只是AttributeSet。系统何时将布局中的AttributeSet解析成LayoutParams的呢?
public LayoutParams generateLayoutParams(AttributeSet attrs) { | |
return new RelativeLayout.LayoutParams(getContext(), attrs); | |
} | |
protected LayoutParams generateDefaultLayoutParams() { | |
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); | |
} |
ViewGroup有个关键的方法,generateLayoutParams()。
public LayoutParams(Context c, AttributeSet attrs) { | |
super(c, attrs); | |
TypedArray a = c.obtainStyledAttributes(attrs, | |
com.android.internal.R.styleable.RelativeLayout_Layout); | |
final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion; | |
mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR || | |
!c.getApplicationInfo().hasRtlSupport()); | |
final int[] rules = mRules; | |
//noinspection MismatchedReadAndWriteOfArray | |
final int[] initialRules = mInitialRules; | |
final int N = a.getIndexCount(); | |
for (int i =; i < N; i++) { | |
int attr = a.getIndex(i); | |
switch (attr) { | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing: | |
alignWithParent = a.getBoolean(attr, false); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf: | |
rules[LEFT_OF] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf: | |
rules[RIGHT_OF] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above: | |
rules[ABOVE] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below: | |
rules[BELOW] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline: | |
rules[ALIGN_BASELINE] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft: | |
rules[ALIGN_LEFT] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop: | |
rules[ALIGN_TOP] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight: | |
rules[ALIGN_RIGHT] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom: | |
rules[ALIGN_BOTTOM] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft: | |
rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE :; | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop: | |
rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE :; | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight: | |
rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE :; | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom: | |
rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE :; | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent: | |
rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE :; | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal: | |
rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE :; | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical: | |
rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE :; | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf: | |
rules[START_OF] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf: | |
rules[END_OF] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart: | |
rules[ALIGN_START] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd: | |
rules[ALIGN_END] = a.getResourceId(attr,); | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart: | |
rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE :; | |
break; | |
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd: | |
rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE :; | |
break; | |
} | |
} | |
mRulesChanged = true; | |
System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT); | |
a.recycle(); | |
} |
这个代码熟悉吧,这就是我们之前讲过的自定义属性啊!没错,xml布局中的属性会先被解析成LayoutParams。那么我问你个问题,你觉得generateLayoutParams()和generateDefaultLayoutParams()的这个LayoutParams是给自己用的呢?还是给它的子控件用的呢?它是给子控件用的。自己的那个直接在构造方法中就从AttributeSet解析出来了。这样你就理解了,为什么RelativeLayout的那些个
android:layout_centerVertical="true" | |
android:layout_alignParentEnd="true" |
怎么全部定义在子控件里面了。然后ViewGroup的addView()方法中就可以带上这个LayoutParams了。
/** | |
* Adds a child view. If no layout parameters are already set on the child, the | |
* default parameters for this ViewGroup are set on the child. | |
* | |
* <p><strong>Note:</strong> do not invoke this method from | |
* {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)}, | |
* {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> | |
* | |
* @param child the child view to add | |
* @param index the position at which to add the child | |
* | |
* @see #generateDefaultLayoutParams() | |
*/ | |
public void addView(View child, int index) { | |
if (child == null) { | |
throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); | |
} | |
LayoutParams params = child.getLayoutParams(); | |
if (params == null) { | |
params = generateDefaultLayoutParams(); | |
if (params == null) { | |
throw new IllegalArgumentException( | |
"generateDefaultLayoutParams() cannot return null "); | |
} | |
} | |
addView(child, index, params); | |
} |
你不重写generateLayoutParams()方法,怎么在添加子控件的时候,让子控件用你的LayoutParams呢?
public static class LayoutParams extends ViewGroup.MarginLayoutParams { | |
} |
以上是LinearLayout.LayoutParams的摘要,我们自定义ViewGroup的时候,是不是也可以继承个ViewGroup的LayoutParams玩一玩呢?然后重写generateLayoutParams()和generateDefaultLayoutParams()方法。
public LayoutParams generateLayoutParams(AttributeSet attrs) { | |
return new LinearLayout.LayoutParams(getContext(), attrs); | |
} |
这里return你的ViewGroup的LayoutParams,然后在你的ViewGroup的LayoutParams的构造方法中就可以解析自定义属性attrs了。如果忘记了解析方式,我给你个提示,使用context的obtainStyledAttributes()方法。
大部分停留在第二层理解,你如果学会了第三层,那么你自定义View又可以玩出新的高度了。