1. 程式人生 > >Android 獲取View的寬高

Android 獲取View的寬高

獲取View的寬高

我們經常會需要獲取View的寬度或者高度,但是我們會發現不管是在onCreate還是onStart,onResume裡面獲取到的值都是0.

這是因為View的measure過程和Activity的生命週期方法不是同步進行的,不能保證在Activity的onCreate,onStart,onResume方法執行時View已經測量完畢,所以不能獲得正確寬高值。

因此我們需要通過其他方法來實現,如下幾種方法都可以獲取正確寬高值

  • Activity/View的onWindowFocusChanged方法體內
  • 利用view.post(runnable)
  • ViewTreeObserver
  • view.measure
  • 自定義View時獲取寬高

Activity/View的onWindowFocusChanged方法體內

這個方法的含義是View已經初始化完畢了,自然可以獲取寬高,但是該方法有可能多次執行,當Activity焦點變化的時候都會被呼叫。

public void onWindowFocusChanged(boolean hasFocus){
    super.onWindowFocusChanged(hasFocus);
    if(hasFocus){
        int width = view.getMeasuredWidth();
        int
height = view.getMeasuredHeight(); } }

view.post(runnable)

通過view的post可以將一個runnable投遞到訊息佇列的尾部,然後等待Looper呼叫此runnable的時候,View也已經初始化好了。

protected void onStart(){
    super.onStart();
    view.post(new Runnable);
        @Override
        public void run() {
        int width = view.getMeasuredWidth();
        int
height = view.getMeasuredHeight(); } }); }

這裡在onCreate 和 onResume裡面都可以。

ViewTreeObserver

顧名思義這個類是整個View樹結構的觀察者,通過它的眾多回調可以獲取到view的寬高,比如onGlobalLayoutListener介面,當View樹的狀體改變或者View樹內部的View的可見性發生改變時該介面都將被回撥,顯然onGlobal也會被多次呼叫.

protected void onResume() {
        super.onResume();
        ViewTreeObserver observer = btn.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                btn.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                int width = btn.getMeasuredWidth();
                int height = btn.getMeasuredHeight();
            }
        });
    }

這裡的removeOnGlobalLayoutListener方法需要API 16。

view.measure

該方法有兩個引數即寬高的MeasureSpec,完整方法名為measure(int widthMeasureSpec,int heightMesureSpec) 如果對此不瞭解的可以先去學習關於view的measure過程.
通過手動對view測量得到寬高,這種方法較為複雜,要分情況處理,根據view的LayoutParams來分

  • match_parent
    根據View的measure過程分析構造此種MeasureSpec需要知道父容器的剩餘空間,而此時無法知道,所以理論上不可能測出View的大小。
  • 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);

int width = view.getMeasuredWidth());
int height = view.getMeasuredHeight());

注意到(1 << 30)-1,通過分析MeasureSpec的實現可以知道,View的尺寸使用30位二進位制表示,也就是說最大是30個1(即2^30 -1),也就是(1 << 30)-1,在最大化模式下我們用View理論上支援的最大值去構造MeasureSpec是合理的。

另外關於View的measure,網路上有兩個錯誤的用法:
1.

int widthMeasureSpec = MeasureSpec.makeMeasureSpec(-1,MeasureSpec.UNSPECIFIED);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(-1,MeasureSpec.UNSPECIFIED);
view.measure(widthMeasureSpec,heightMeasureSpec);

2.

view.measure(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);

自定義View時獲取寬高

有時我們在自定義控制元件的時候會需要用到寬高值,大部分的時候可以在onLayout方法裡面獲取到真實寬高值,同樣也可以用上述第三種方法獲取。首先讓自定義的View實現onGlobalListener介面,需要實現onGlobalLayout方法.

然後在onAttachedToWindow()新增

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

記得要在onDetachedFromWindow()刪除

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeOnGlobalLayoutListener(this);(API 16)
    }

接著在onGlobalLayout方法中獲取寬高即可,因為要呼叫多次,可以設立個標誌位.

    private boolean isFirst = true;
    @Override
    public void onGlobalLayout() {
        if (isFirst) {
            int height = getHeight();
            isFirst = false;
        }
    }