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;
}
}