【轉】Android 獲取 View 寬高的常用正確方式,避免為零

原文地址: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() 方法用的更多一些。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末翘鸭,一起剝皮案震驚了整個濱河市滴铅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌就乓,老刑警劉巖汉匙,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異生蚁,居然都是意外死亡噩翠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門邦投,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伤锚,“玉大人,你說我怎么就攤上這事志衣。” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵典挑,是天一觀的道長窖维。 經(jīng)常有香客問我,道長绿店,這世上最難降的妖魔是什么吉懊? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任庐橙,我火速辦了婚禮,結果婚禮上借嗽,老公的妹妹穿的比我還像新娘态鳖。我一直安慰自己,他們只是感情好淹魄,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布郁惜。 她就那樣靜靜地躺著,像睡著了一般甲锡。 火紅的嫁衣襯著肌膚如雪兆蕉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天缤沦,我揣著相機與錄音虎韵,去河邊找鬼。 笑死缸废,一個胖子當著我的面吹牛包蓝,可吹牛的內容都是我干的。 我是一名探鬼主播企量,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼测萎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了届巩?” 一聲冷哼從身側響起硅瞧,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恕汇,沒想到半個月后腕唧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡瘾英,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年枣接,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缺谴。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡但惶,死狀恐怖,靈堂內的尸體忽然破棺而出瓣赂,到底是詐尸還是另有隱情榆骚,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布煌集,位于F島的核電站妓肢,受9級特大地震影響,放射性物質發(fā)生泄漏苫纤。R本人自食惡果不足惜碉钠,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一纲缓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧喊废,春花似錦祝高、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瓣蛀,卻和暖如春陆蟆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惋增。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工叠殷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诈皿。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓林束,卻偏偏與公主長得像,于是被迫代替她去往敵國和親稽亏。 傳聞我的和親對象是個殘疾皇子壶冒,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內容