衆所周知,在Android實際開發中,對於某些複雜多變的狀況,控件的位置擺放、大小控制並不是是xml類型的layout文件徹底能夠搞定的。此時,咱們一般會使用Java代碼來經過動態計算,將指定的控件擺放在相應的位置,並限定其大小。一樣地,也須要獲取某個控件的大小。 對於獲取控件寬、高的方法,你們能夠自行谷歌或者百度,大抵無非一下三種方法:android
對於上述第三種狀況,咱們暫且不論。對於前兩者而言,有沒有更簡單的實現呢?git
對於初學者,可能會有這樣的疑問:爲何咱們不能在onCreate()或者onResume()中直接使用上述第三種方案獲取寬高呢?
結論是:那樣的話,獲取來的值極可能皆爲0,即便實際的寬高不是0。那麼這是爲什麼呢?
這實際上是由Android的UI繪製流程決定的。你們不妨試着作一下實驗,即便是在onResume()方法後,它的意義也僅僅是指Activity進入了可見的狀態,這並不意味着界面繪製的結束。咱們能夠用一個簡單的帶有寬高值得View來作實驗,觀察Activity中各回調方法的調用順序,獲得的結果將是這樣的:github
Activity.oncreate() → Activity.onResume() → View.onMeasure() → View.onLayout() → onGlobalLayoutListener() → Activity.onWidnowFocusChanged() → ... → View.onDraw() -> ...bash
所以,若是咱們在onResume()中嘗試獲取View寬高的話,很大機率是會失敗的。異步
這裏咱們開門見山地先放上代碼片:ide
private int[] measureView(final View view) {
final int[] returnData = new int[2];
view.post(new Runnable() {
@Override
public void run() {
returnData[0] = view.getWidth();
returnData[1] = view.getHeight();
Log.i(TAG, "Width: " + returnData[0] + ", height: " + returnData[1]);
}
});
return returnData;
}
複製代碼
上述代碼做爲通用的方法將獲取任意View的寬高作了封裝,其妙處就在‘view.post’處。 將其置於onCreate()、onResume()方法中調用,都可獲取到正確的寬高。post
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate start!");
setContentView(R.layout.activity_main);
testTv = (TextView) findViewById(R.id.testTv);
measureView(testTv);
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume start!");
measureView(testTv);
}
複製代碼
Logcat中的運行結果:ui
2019-01-14 22:33:13.874 18355-18355/com.example.wenhan.helloandroid I/MainActivity: Width: 225, height: 57
2019-01-14 22:33:13.874 18355-18355/com.example.wenhan.helloandroid I/MainActivity: Width: 225, height: 57spa
其中的玄機在於,咱們在View.post()中所寫的語句並無當即執行,而在其真正執行的時候,View的寬高已經被測量完成了,那時咱們再去獲取寬高時,就會很容易地獲取到正確的值了。 經過斷點Debug,能夠輕鬆地發現,在Activity啓動過程的調用棧中,存在ActivityThread類被執行了,具體按照:線程
main() -> handleResumeActivity() -> addView() -> setView() -> requestLayout() -> scheduleTraversals() -> 執行mTraversalRunnable異步線程 -> doTraversal() -> performTraversals() -> ... -> performMeasure() -> ...
的執行順序。
在咱們獲取寬高的語句執行前,主線程的Handler正在執行TraversalRunnable(見上述方法具體實現),而performMeasure也被包含其中。又由於咱們獲取寬高的語句要排隊,處於等待狀態,直到主線程Handler輪到執行咱們的語句,而此時View的寬高的測量已經結束。
完整示例代碼:github.com/wh1990xiao2…