一、前言:
實(shí)際上在 onCreate、onStart、onResume中均無(wú)法正確得到某個(gè) View 的寬/高信息,這是因?yàn)?View 的 measure 過(guò)程和 Activity 的生命周期方法不是同步執(zhí)行的,因此無(wú)法保證 Activity 執(zhí)行了onCreate、onStart、onResume時(shí)某個(gè) View 已經(jīng)測(cè)量完畢。如果沒(méi)有測(cè)量完畢,那么獲得的寬/高就是 0。
當(dāng)我們動(dòng)態(tài)創(chuàng)建某些View時(shí),需要通過(guò)獲取他們的width和height來(lái)確定別的View的布局,但是在onCreate()獲取view的width和height會(huì)得到0。View.getWidth()和View.getHeight()為0的根本原因是控件還沒(méi)有完成繪制,你必須等待系統(tǒng)將繪制完View時(shí)才能獲得。
二、解決方案:
1. Activity/View#onWindowFocusChanged
onWindowFocusChanged這個(gè)方法的含義是:View 已經(jīng)初始化完畢,寬/高已經(jīng)準(zhǔn)備好了,這個(gè)時(shí)候去獲取寬/高時(shí)沒(méi)問(wèn)題的。需要注意的是,onWindowFocusChanged會(huì)被調(diào)用多次,當(dāng) Activity 的窗口得到焦點(diǎn)和失去焦點(diǎn)時(shí),均會(huì)被調(diào)用一次。如果頻繁地進(jìn)行 onResume 和 onPause,那么onWindowFocusChanged也會(huì)頻繁地調(diào)用。
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus){
int width = btn_weak.getMeasuredWidth();
int height = btn_weak.getMeasuredHeight();
}
}
2. 監(jiān)聽(tīng)Draw/Layout事件:ViewTreeObserver
ViewTreeObserver監(jiān)聽(tīng)很多不同的界面繪制事件。一般來(lái)說(shuō)OnGlobalLayoutListener就是可以讓我們獲得到view的width和height的地方.下面onGlobalLayout內(nèi)的代碼會(huì)在View完成Layout過(guò)程后調(diào)用。
注意:伴隨著 View 樹(shù)的狀態(tài)改變等,onGlobalLayout會(huì)被調(diào)用多次。
ViewTreeObserver observer = view.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
btn_weak.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int width = view.getWidth();
int height = view.getHeight();
}
});
3. View.post(runnable)
通過(guò) post 可以將一個(gè) runnable 投遞到消息隊(duì)列的尾部,然后等待 Looper 調(diào)用此 runnable 的時(shí)候,View 也已經(jīng)初始化好了。
UI事件隊(duì)列會(huì)按順序處理事件。在setContentView()被調(diào)用后,事件隊(duì)列中會(huì)包含一個(gè)要求重新layout的message,所以任何你post到隊(duì)列中的東西都會(huì)在Layout發(fā)生變化后執(zhí)行。
view.post(new Runnable() {
@Override
public void run() {
int width = view.getWidth();
int height = view.getHeight();
}
});
這個(gè)方法比ViewTreeObserver好:
1、你的代碼只會(huì)執(zhí)行一次,而且你不用在在每次執(zhí)行后將Observer禁用,省心多了。
2、語(yǔ)法很簡(jiǎn)單
4. 重寫(xiě)View的onLayout方法
這個(gè)方法只在某些場(chǎng)景中實(shí)用,比如當(dāng)你所要執(zhí)行的東西應(yīng)該作為他的內(nèi)在邏輯被內(nèi)聚、模塊化在view中,否者這個(gè)解決方案就顯得十分冗長(zhǎng)和笨重。
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);
view.getHeight(); //height is ready
}
};
需要注意的是onLayout方法會(huì)調(diào)用很多次,所以要考慮好在這個(gè)方法中要做什么,或者在第一次執(zhí)行后禁用掉你的代碼。
5. 獲取View固定寬高
如果你要獲取的View的width和height是固定的,那么你可以直接使用:
View.getMeasureWidth()
View.getMeasureHeight()
但是要注意,這兩個(gè)方法所獲取的width和height可能跟實(shí)際draw后的不一樣。官方文檔解釋了不同的原因:
View的大小由width和height決定。一個(gè)View實(shí)際上同時(shí)有兩種width和height值。
第一種是measure width和measure height。他們定義了view想要在父View中占用多少width和height(詳情見(jiàn)Layout)。measured height和width可以通過(guò)getMeasuredWidth() 和 getMeasuredHeight()獲得。
第二種是width和height,有時(shí)候也叫做drawing width和drawing
height。這些值定義了view在屏幕上繪制和Layout完成后的實(shí)際大小。這些值有可能跟measure
width和height不同。width和height可以通過(guò)getWidth()和getHeight獲得。
參考鏈接:https://blog.csdn.net/lufengdie/article/details/48003631