原文地址:http://blog.csdn.net/growing_tree/article/details/69220191
相信有很多朋友都有過在 Activity 中通過 getWidth() 之類的方法獲取 View 的寬高值衬潦,可能在 onCreate() 生命周期方法中霞溪,也可能在 onResume() 生命周期方法中。然而抹凳,不幸的是,并不能獲取所要的結果伦腐,寬高值均為 0赢底。
如果對 View 的繪制顯示流程熟悉的話,就會知道問題所在。我們知道幸冻,在自定義 View 時粹庞,通常都要重寫 onMeasure、onLayout洽损、onDraw 這幾個方法庞溜。同理,Activity 的內容顯示到設備上時趁啸,這些 View 也要經(jīng)歷這些階段强缘。
所以,當我們在 Activity 的生命周期方法中直接獲取 View 的寬高時不傅,View 也許還沒執(zhí)行完 measure 階段,那么自然獲取到的寬高結果為 0赏胚。這也提醒我們一點访娶,在 onCreate 方法中只適合做些一些初始化設置工作,使用 View 執(zhí)行動畫或者其他操作時觉阅,一定要注意考慮 View 繪制的耗時過程崖疤。
那么問題來了,怎么樣才能在 Activity 代碼中獲取到 View 的實際寬高值呢典勇?這里給大家總結一些常用方法劫哼。
addOnGlobalLayoutListener
view.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= 16) {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
int width = view.getWidth();
}
});
ViewTreeObserver,顧名思義割笙,視圖樹的觀察者权烧,可以監(jiān)聽 View 的全局變化事件。比如伤溉,layout 變化般码,draw 事件等÷夜耍可以閱讀源碼了解更多事件板祝。
注意:使用時需要注意及時移除該事件的監(jiān)聽,避免后續(xù)每一次發(fā)生全局 View 變化均觸發(fā)該事件走净,影響性能券时。這里用的是 OnGlobalLayoutListener,移除監(jiān)聽時注意 API 的版本兼容處理伏伯。
addOnPreDrawListener
view.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
view.getViewTreeObserver().removeOnPreDrawListener(this);
int width = view.getWidth(); return false;
}
});
這里同樣是利用 ViewTreeObserver 觀察者類橘洞,只不過這里監(jiān)聽的是 draw 事件。
view.post()
view.post(new Runnable() {
@Override
public void run() {
int width = view.getWidth();
}
});
利用 Handler 通信機制舵鳞,添加一個 Runnable 到 message queue 中震檩,當 view layout 處理完成時,自動發(fā)送消息,通知 UI 線程抛虏。借此機制博其,巧妙獲取 View 的寬高屬性。代碼簡潔迂猴,使用簡單慕淡,相比 ViewTreeObserver 監(jiān)聽處理,還不需要手動移除觀察者監(jiān)聽事件沸毁。
onLayout()
view = new View(this) {
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
int = view.getWidth();
}
};
利用 View 繪制過程中的 onLayout() 方法獲取寬高值峰髓,這也是為一個不需要借助其他類或者方法,僅靠自己就能完成獲取寬高屬性的手段息尺。但是局限性在于携兵,在 Activity 中使用代碼創(chuàng)建 View 的場景較少,一般都是獲取 layout 文件所加載 View 的寬高搂誉。
addOnLayoutChangeListener
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
view.removeOnLayoutChangeListener(this);
int width = view.getWidth();
}
});
監(jiān)聽 View 的 onLayout() 繪制過程徐紧,一旦 layout 觸發(fā)變化,立即回調 onLayoutChange
方法炭懊。注意并级,用完也要注意調用 remove 方法移除監(jiān)聽事件。
ViewCompat.isLaidOut(view)
if (ViewCompat.isLaidOut(view)) {
int width = view.getWidth();
}
嚴格意義上來講侮腹,這不能作為一個獲取寬高的方式之一嘲碧。充其量只能是一個判斷條件。只有當 View 至少經(jīng)歷過一次 layout 時父阻,isLaidOut() 方法才能返回 true愈涩,繼而才能獲取到 View 的真實寬高。所以至非,當我們的代碼中有多次調用獲取寬高時钠署,才有可能使用這個方法判斷處理。
getMeasuredWidth()
最后荒椭,借此地兒補充一點知識谐鼎,getMeasuredWidth() 與 getWidth() 或者 getMeasuredHeight() 與 getHeight() 的區(qū)別。很多人容易對此產(chǎn)生混淆趣惠,不知道這兩個方法到底有什么區(qū)別狸棍,使用時應該如何取舍。其實味悄,官方文檔介紹 View class 時草戈,對于 Size 部分,有這么一段話:
The size of a view is expressed with a width and a height. A view actually possess two pairs of width and height values.
The first pair is known as measured width and measured height. These dimensions define how big a view wants to be within its parent (see Layout for more details.) The measured dimensions can be obtained by calling getMeasuredWidth() and getMeasuredHeight().
The second pair is simply known as width and height, or sometimes drawing width and drawing height. These dimensions define the actual size of the view on screen, at drawing time and after layout. These values may, but do not have to, be different from the measured width and height. The width and height can be obtained by calling getWidth() and getHeight().
這段話足以解釋 getMeasuredXXX() 與 getXXX() 的區(qū)別和聯(lián)系所在侍瑟。說得直白一點唐片,measuredWidth 與 width 分別對應于視圖繪制 的 measure 與 layout 階段丙猬。很重要的一點是,我們要明白费韭,View 的寬高是由 View 本身和 parent 容器共同決定的茧球,要知道有這個 MeasureSpec 類的存在。
比如星持,View 通過自身 measure() 方法向 parent 請求 100x100 的寬高抢埋,那么這個寬高就是 measuredWidth 和 measuredHeight 值。但是督暂,在 parent 的 onLayout() 階段揪垄,通過 childview.layout() 方法只分配給 childview 50x50 的寬高。那么逻翁,這個 50x50 寬高就是 childview 實際繪制并顯示到屏幕的寬高饥努,也就是 width 和 height 值。
如果你對自定義 View 過程很熟練的話卢未,理解這部分內容就比較輕松一些肪凛。事實上,開發(fā)過程中辽社,getWidth() 和 getHeight() 方法用的更多一些。