自定義View--View的measure過程

  • DecorView

窗口的頂級布局:包含了titlebar(標(biāo)題欄)、content(內(nèi)容android.R.id.content)
通過android.R.id.content獲得內(nèi)容布局的Parent層:

ViewGroup content = (ViewGroup)findViewById(android.R.id.content)  ;
View view = content.getChildAt(0);

View的measure過程

  • MeasureSpec 代表32位的int值,高2位代表SpecMode(測量模式) ,低30位代表SpecSize(測量模式下的規(guī)格大小)

1.UNSPECIFIED
父容器不對Viewyou任何限制,要多大給多大,這種情況一般用于系統(tǒng)內(nèi)部,表示一種測量的狀態(tài),一般屬于系統(tǒng)內(nèi)部多次Measure的情形,一般來說不需要關(guān)注此模式

2.EXACTLY
父容器已經(jīng)檢測出View所需要的精確大小,這個(gè)時(shí)候View的最終大小就是SpecSize所指定的的值,他對應(yīng)于LayoutParams中的match_parent 和具體的數(shù)值這兩種模式

3.AT_MOST
父容器指定了一個(gè)可用大小即SpecSize,View 的大小不能大于這個(gè)值具體是什么要看不同View的具體實(shí)現(xiàn)。他對應(yīng)于LayoutParams中的wrap_content

parentSpecMode EXACTLY AT_MOST UNSPECIFIED
chlidLayoutParams
dp/px(固定數(shù)值) EXACTLY childSize EXACTLY childSize EXACTLY childSize
match_parent EXACTLY parentSize AT_MOST parentSize UNSPECIFIED 0
wrap_content AT_MOST parentSize AT_MOST parentSize UNSPECIFIED 0

從上邊的表中可以看出View的match_parent 和 wrap_content 是一樣的結(jié)果都是parent剩余的大小,這樣跟我們的實(shí)際結(jié)果是不一樣的,實(shí)際上在View這種情況先是進(jìn)行了一定的處理的
具體代碼是這樣的:

public class mView extends View {
    public mView(Context context) {
        super(context);
    }

    private int mWidth = 0;
    private int mHeight = 0;

    @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(mWidth, mHeight);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(mWidth, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, mHeight);
        }
    }
}

  • 在measure之后通過getMeasureHeight/Height ,但是盡量不要在measure之后獲得,好的習(xí)慣是在onLayout中獲得,但是有的時(shí)候我們在Activity的onCreat中獲得measure寬高,但是View沒有測完,所以為0,現(xiàn)在解決方案有4種:
    • Activity /View#onWindowFocusChanged
@Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        if (hasWindowFocus) {
            int width = getMeasuredWidth();
            int height = getMeasuredHeight();
        }
    }
  • view.post(runnable)
 @Override
    protected void onStart() {
        super.onStart();
        view.post(new Runnable() {
            @Override
            public void run() {
                int width = view.getMeasuredWidth();
                int height = view.getMeasuredHeight();
            }
        });
        
        Log.e(TAG, "onStart()");
    }
  • ViewTreeObserver
 ViewTreeObserver observer = view.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

            @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout() {
                view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                int width = view.getMeasuredWidth();
                int height = view.getMeasuredHeight();
            }
        });
  • view.measure(int widthMeasureSpec, int heightMeasureSpec)
    這種比較復(fù)雜,根據(jù)View的LayoutParams來分:
    • match_parent
      還是直接放棄吧
    • wrap_content
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
     int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
     view.measure(widthMeasureSpec, heightMeasureSpec);

注意到(1<<30)-1 ,通過分析MeasureSpec的實(shí)現(xiàn)可以知道,View的尺寸使用30位二進(jìn)制表示也就是說最大是30個(gè)1(即2^30 -1 ),也就是(1<<30)-1,在最大化模式下,我們用View理論上能支持的最大值去構(gòu)造MeasureSpec是合理的
+ 具體數(shù)值(dp/px)

 //比如寬/高 都是100px 如下measure
       int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
       int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
       view.measure(widthMeasureSpec,heightMeasureSpec);

錯(cuò)誤用法:無法通過錯(cuò)誤的MeasureSpec去得出合法的SpecMode,從而導(dǎo)致measure過程出錯(cuò)

int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec( -1, View.MeasureSpec.UNSPECIFIED);
        int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec( -1, View.MeasureSpec.UNSPECIFIED);
        view.measure(widthMeasureSpec, heightMeasureSpec);
view.measure(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容