昨天爬了一天的山~,我的腳已經(jīng)崩潰了所禀,嘻嘻不過還是挺開心的界斜,今天我們要總結(jié)下View的measure(測量)ViewGroup的measure
我們平常繼承View 和ViewGroup 這2個方法呢,都是會有一個測量的步驟的,如果只是一個View那么measure完了就完了窥翩,如果是ViewGroup不僅自己要測量下业岁,自己的子View也要測量下。
View里面的measure
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
boolean optical = isLayoutModeOptical(this);
.....省略
if (forceLayout || needsLayout) {
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
//我們直接看這里鳍烁,因為這里是設(shè)置布局大小的地方
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
// Casting a long to int drops the high 32 bits, no mask needed
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
.....省略
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
//setMeasuredDimension 方法直接設(shè)置寬高了
}
getDefaultSize方法主要返回的是一個大小值(寬度和高度)
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);//測量后的大小
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
//不管你是wrap還是match最后的結(jié)果由specSize決定
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
UNSPECIFIED測量模式我們先不理它叨襟,其它2種的測量模式最后的結(jié)果都是specSize,就是View測量后的大小
我們這里面還有一個getSuggestedMinimumWidth()和getSuggestedMinimumHeight()2個方法幔荒,我們看其中一個
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
//如果backgroud(背景)==null,我們就取mMinWidth糊闽,否則我們就去后面2個比較的那個最大值
//這里的mMinWidth==android:minWidth,當然如果我們沒有去指定的話爹梁,mMinWidth=0了
我們點擊mBackground.getMinimumWidth()這個方法看
public int getMinimumWidth() {
final int intrinsicWidth = getIntrinsicWidth();
return intrinsicWidth > 0 ? intrinsicWidth : 0;
}
現(xiàn)在我們知道了一個View不管你設(shè)置wrap還是match右犹,最后的結(jié)果都是由
specSize 它決定,現(xiàn)在有一個問題來了姚垃,當我們自定義View的時候念链,我們平常寫寬度/高度為wrap,我們會在腦海中想象他是個包裹的狀態(tài)积糯,但是當我們真正去實驗的時候掂墓,發(fā)現(xiàn)他卻是全屏的狀態(tài)(match),這個就讓我們很詫異看成,很疑惑?君编。
現(xiàn)在我們目前只知道2點
1、我們不管是wrap還是match最后的結(jié)果都是由specSize決定你的大小(代碼可以看出來)
2川慌、Linlayout在繪制布局的時候會調(diào)用measureChildWithMargins方法吃嘿,在這個方法里面會調(diào)用child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
當然第二點我們后面再講到
如果你看過我對MeasureSpece的總結(jié)的話你應(yīng)該就知道了
我們可以明白這一點,Linlayout(父布局)決定了子布局的大小梦重,本文中specSize的大小由父容器來給你決定兑燥,他決定你你有多大的使用空間(父容器剩余的空間),我們可以通過上面的圖片知道琴拧,如果我們的 childLayoutParams的參數(shù)是wrap_content那么我們不管父容器是wrap還是match,子容器的測量模式都是AT_MOST降瞳,但是它的寬高大小都是parentSize的大小,也就是父容器的大邪丁(你要明白這一點力崇,你需要看我前面的學習總結(jié))
下面這個例子很好的展示了當子容器是wrap的時候,沾滿全屏的情況
現(xiàn)在大家看到的這個效果是一個Linlayout赢织,嵌套一個自定義的view
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<scanning.mobile.com.viewstudy.viewStu.st
android:background="@color/colorAccent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
但是我嵌套里面的寬/高都是wrap_content,但是我們卻看到的是沾滿全屏了
這個是為啥亮靴?我們可以采用剛剛我們學到的知識來解釋,
我們拿著我們剛剛參考那張表格來看
首先父容器是match_parent于置,那么對應(yīng)的測量模式是:EXACTLY(精確模式)茧吊,子容器是wrap_content贞岭,那么它對應(yīng)的模式就是AT_MOST
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);//父容器的大小
int size = Math.max(0, specSize - padding);//獲取剩下的空間
else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
我們知道我們最后的size就是Math.max(0, specSize - padding);//獲取剩下的空間,
所以我們就是父容器的大小了搓侄,那么問題了來了瞄桨,我們要如何來解決這樣的問題?
public class st extends View{
private static final String TAG = st.class.getSimpleName();
public st(Context context) {
super(context);
}
public st(Context context, AttributeSet attrs) {
super(context, attrs);
}
public st(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
int height=200;
int width = 200;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){//他會進入這里讶踪,所以我們在這里直接來解決
setMeasuredDimension(width,height);
}else if (widthSpecMode==MeasureSpec.AT_MOST){
setMeasuredDimension(width,heightSpecSize);
}else if (heightSpecMode == MeasureSpec.AT_MOST){
setMeasuredDimension(height,widthSpecSize);
}
}
}
獲取它的測量模式芯侥,根據(jù)測量模式來修改
學習資源書籍:《Android 開發(fā)藝術(shù)探索》