Android 獲取 View 寬高的常用正確方式洋只,避免為零

相信有很多朋友都有過在 Activity 中通過 getWidth() 之類的方法獲取 View 的寬高值戒傻,可能在 onCreate() 生命周期方法中脑奠,也可能在 onResume() 生命周期方法中。然而赖钞,不幸的是腰素,并不能獲取所要的結(jié)果,寬高值均為 0雪营。

如果對(duì) View 的繪制顯示流程熟悉的話弓千,就會(huì)知道問題所在。我們知道献起,在自定義 View 時(shí)洋访,通常都要重寫 onMeasure、onLayout谴餐、onDraw 這幾個(gè)方法姻政。同理,Activity 的內(nèi)容顯示到設(shè)備上時(shí)岂嗓,這些 View 也要經(jīng)歷這些階段汁展。

所以,當(dāng)我們?cè)?Activity 的生命周期方法中直接獲取 View 的寬高時(shí)厌殉,View 也許還沒執(zhí)行完 measure 階段食绿,那么自然獲取到的寬高結(jié)果為 0。這也提醒我們一點(diǎn)年枕,在 onCreate 方法中只適合做些一些初始化設(shè)置工作炫欺,使用 View 執(zhí)行動(dòng)畫或者其他操作時(shí),一定要注意考慮 View 繪制的耗時(shí)過程熏兄。

那么問題來了品洛,怎么樣才能在 Activity 代碼中獲取到 View 的實(shí)際寬高值呢?這里給大家總結(jié)一些常用方法摩桶。

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 事件等士飒。可以閱讀源碼了解更多事件蔗崎。

注意:使用時(shí)需要注意及時(shí)移除該事件的監(jiān)聽酵幕,避免后續(xù)每一次發(fā)生全局 View 變化均觸發(fā)該事件,影響性能缓苛。這里用的是 OnGlobalLayoutListener芳撒,移除監(jiān)聽時(shí)注意 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 通信機(jī)制芥备,添加一個(gè) Runnable 到 message queue 中,當(dāng) view layout 處理完成時(shí)舌菜,自動(dòng)發(fā)送消息萌壳,通知 UI 線程。借此機(jī)制酷师,巧妙獲取 View 的寬高屬性讶凉。代碼簡(jiǎn)潔,使用簡(jiǎn)單山孔,相比 ViewTreeObserver 監(jiān)聽處理懂讯,還不需要手動(dòng)移除觀察者監(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() 方法獲取寬高值台颠,這也是為一個(gè)不需要借助其他類或者方法褐望,僅靠自己就能完成獲取寬高屬性的手段。但是局限性在于串前,在 Activity 中使用代碼創(chuàng)建 View 的場(chǎng)景較少瘫里,一般都是獲取 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ā)變化谨读,立即回調(diào) onLayoutChange 方法。注意坛吁,用完也要注意調(diào)用 remove 方法移除監(jiān)聽事件劳殖。

ViewCompat.isLaidOut(view)


if (ViewCompat.isLaidOut(view)) {
    int width = view.getWidth();
}

嚴(yán)格意義上來講,這不能作為一個(gè)獲取寬高的方式之一拨脉。充其量只能是一個(gè)判斷條件哆姻。只有當(dāng) View 至少經(jīng)歷過一次 layout 時(shí),isLaidOut() 方法才能返回 true玫膀,繼而才能獲取到 View 的真實(shí)寬高矛缨。所以,當(dāng)我們的代碼中有多次調(diào)用獲取寬高時(shí)帖旨,才有可能使用這個(gè)方法判斷處理箕昭。

getMeasuredWidth()


最后,借此地兒補(bǔ)充一點(diǎn)知識(shí)解阅,getMeasuredWidth() 與 getWidth() 或者 getMeasuredHeight() 與 getHeight() 的區(qū)別落竹。很多人容易對(duì)此產(chǎn)生混淆瓮钥,不知道這兩個(gè)方法到底有什么區(qū)別筋量,使用時(shí)應(yīng)該如何取舍。其實(shí)碉熄,官方文檔介紹 View class 時(shí)桨武,對(duì)于 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)系所在呀酸。說得直白一點(diǎn)性誉,measuredWidth 與 width 分別對(duì)應(yīng)于視圖繪制 的 measure 與 layout 階段。很重要的一點(diǎn)是茎杂,我們要明白错览,View 的寬高是由 View 本身和 parent 容器共同決定的,要知道有這個(gè) MeasureSpec 類的存在煌往。

比如倾哺,View 通過自身 measure() 方法向 parent 請(qǐng)求 100x100 的寬高,那么這個(gè)寬高就是 measuredWidth 和 measuredHeight 值刽脖。但是羞海,在 parent 的 onLayout() 階段,通過 childview.layout() 方法只分配給 childview 50x50 的寬高曲管。那么却邓,這個(gè) 50x50 寬高就是 childview 實(shí)際繪制并顯示到屏幕的寬高,也就是 width 和 height 值院水。

如果你對(duì)自定義 View 過程很熟練的話腊徙,理解這部分內(nèi)容就比較輕松一些。事實(shí)上衙耕,開發(fā)過程中昧穿,getWidth() 和 getHeight() 方法用的更多一些。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末橙喘,一起剝皮案震驚了整個(gè)濱河市时鸵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌厅瞎,老刑警劉巖饰潜,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異和簸,居然都是意外死亡彭雾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門锁保,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薯酝,“玉大人半沽,你說我怎么就攤上這事∥獠ぃ” “怎么了者填?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)做葵。 經(jīng)常有香客問我占哟,道長(zhǎng),這世上最難降的妖魔是什么酿矢? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任榨乎,我火速辦了婚禮,結(jié)果婚禮上瘫筐,老公的妹妹穿的比我還像新娘蜜暑。我一直安慰自己,他們只是感情好策肝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布史煎。 她就那樣靜靜地躺著,像睡著了一般驳糯。 火紅的嫁衣襯著肌膚如雪篇梭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天酝枢,我揣著相機(jī)與錄音恬偷,去河邊找鬼。 笑死帘睦,一個(gè)胖子當(dāng)著我的面吹牛袍患,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播竣付,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼诡延,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了古胆?” 一聲冷哼從身側(cè)響起肆良,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逸绎,沒想到半個(gè)月后惹恃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棺牧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年巫糙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颊乘。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡参淹,死狀恐怖醉锄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情浙值,我是刑警寧澤榆鼠,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站亥鸠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏识啦。R本人自食惡果不足惜负蚊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望颓哮。 院中可真熱鬧家妆,春花似錦、人聲如沸冕茅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姨伤。三九已至哨坪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乍楚,已是汗流浹背当编。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留徒溪,地道東北人忿偷。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像臊泌,于是被迫代替她去往敵國(guó)和親鲤桥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容