最近看開源項目時,往往對一些以前熟悉(現(xiàn)在模糊)的知識,導(dǎo)致無法繼續(xù)思考.
LayoutParams是什么?
LayoutParams可以理解為是子控制在父容器中的布局信息對象 ,它封裝了子控件擺放的位置、高、寬等信息。如:屏幕一塊區(qū)域被子控件使用,將一個子控件添加到一個父容器中,我們需要告訴父容器布局的擺放位置,那么子控件可以通過LayoutParams封裝信息通知父布局 。

如:上圖launcher界面 ,每個App圖標(biāo)都占據(jù)一個位置,也就是App圖標(biāo)都有一個位置的信息,這些位置信息及圖標(biāo)大小就是LayoutParams。
- LayoutParams只是封裝了寬高,寬和高可以設(shè)置三種值:
- 固定的值;
- MATCH_PARENT,填滿(和父容器一樣大?。?/li>
- WRAP_CONTENT,能裹住組件就好。
可以簡單理解:LayoutParams就是子布局在父布局的一個信息位置對象。
倆個布局演示:
relativeLayout = (RelativeLayout) findViewById(R.id.main);
Button button = new Button(this);
button.setText("button");
button.setTextColor(Color.WHITE);
button.setBackgroundColor(Color.BLACK);
FrameLayout.LayoutParams lytp = new FrameLayout.LayoutParams(200, 200);
lytp.setMargins(200,200,0,0);
button.setLayoutParams(lytp);
relativeLayout.addView(button);

relativeLayout = (RelativeLayout) findViewById(R.id.main);
RelativeLayout relative = new RelativeLayout(this);
relative.setBackgroundColor(Color.WHITE);
RelativeLayout.LayoutParams lp=new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CO NTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.CENTER_IN_PARENT);
relative.setLayoutParams(lp);
relativeLayout.addView(relative);

這個ViewGroup怎么沒有,自己想想怎么回事?

View的MeasureSpec測量過程
在自定義View時,我們需要根據(jù)需求來控制View的尺寸。這時候我們需要重寫onMeasure進(jìn)行定制。而如何定制與MeasureSpec有很大關(guān)系,我們接下來進(jìn)行分析。
我們利用debug來查看onMeasure的調(diào)用過程:

我們主要查看measureChildWithMargins方法
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
//在測量中會根據(jù)View的Layoutparams與父容器所施加的規(guī)
//則轉(zhuǎn)化成對應(yīng)子布局的MeasureSpec。
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
//然后根據(jù)獲取的子布局的 WidthMeasureSpec 和 HeightMeasureSpec
//對子 view 進(jìn)行測量,這里會最終調(diào)用view的onMeasure進(jìn)行測量
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
上面可以看出WuXiaoView的LayoutParams 與父布局有緊密的關(guān)系。
通過getChildMeasureSpec()源碼查看他們的關(guān)系:
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);//父控件的測量模式
int specSize = MeasureSpec.getSize(spec);//父控件的測量大小
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// 父容器精確模式.
case MeasureSpec.EXACTLY:
//子布局參數(shù)是固定值,比如"layout_width" = "25px"
//那么子布局測量模式肯定是EXACTLY,測量的寬就是25px,
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
//子控件的布局參數(shù)是"match_parent",也就是想占滿父容器
//而此時父容器是精確模式,也就是能確定自己的尺寸了,那子控件也能確定自己大小了
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
//子控件的布局參數(shù)是"wrap_content",也可以重寫根據(jù)自己的邏輯決定自己大小(大小肯定不能大于父容器的大小)
//測量模式就是AT_MOST,測量大小就是父容器的size
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// 父控件最大模式(父控件還不知道自己的尺寸)
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
//子控件能確定自己大小,盡管父容器自己還不知道自己大小
//所以優(yōu)先設(shè)置子布局
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
//子控件想要和父容器一樣大,但父容器也不明確自己大小
//那么子控件也是AT_MOST,并且最大值不會超過父容器大小
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
//子控件根據(jù)自己邏輯決定大小(默認(rèn)最大值不會超過父容器大小)
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
里面很多地方用到了MeasureSpec,接下來看看它的真面目。
MeasureSpec是什么?
sdk解釋:MeasureSpc類封裝了父View傳遞給子View的布局(layout)要求。每個MeasureSpc實例代表寬度或者高度。
MeasureSpc的源碼很簡潔:
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY = 1 << MODE_SHIFT;
public static final int AT_MOST = 2 << MODE_SHIFT;
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
........
}
包含了View的三種測量模式:
測量模式為int類型(32bit),其中高2位用來封裝MeasureMode。
- UNSPECIFIED - 00000000 00000000 00000000 00000000 父容器不對子布局有任何限制,要多大給多大(如: scrollview)
- EXACTLY - 01000000 00000000 00000000 00000000 父容器已經(jīng)測量出子布局大小。
- AT_MOST - 10000000 00000000 00000000 00000000 父窗口限定了一個最大值給子子布局。
低30位用來封裝size.
應(yīng)用:
當(dāng)父容器為wrap_content,子布局也是wrap_content的情況默認(rèn)寬高為600x600。這時候需要重寫MeasureSpec進(jìn)行判斷了,從上面我們分析,我們知道如果不自己設(shè)置子布局為剩余父容器大小

這不是我想要的。
應(yīng)用布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.wuxiao.wuxiaodemo.WuXiaoView
android:background="@color/colorAccent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
WuXiaoView 布局部分代碼
public class WuXiaoView extends View {
...
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthspecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthspecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightspecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightspecSize = MeasureSpec.getSize(heightMeasureSpec);
switch (widthspecMode) {
case MeasureSpec.EXACTLY:
width =widthspecSize;
break;
case MeasureSpec.AT_MOST:
width =600;
break;
}
switch (heightspecMode) {
case MeasureSpec.EXACTLY:
height =heightspecSize;
break;
case MeasureSpec.AT_MOST:
height =600;
break;
}
setMeasuredDimension(width,height);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawText("wuxiao",width/2,height/2,paint);
}
}
