淺析View的requestLayout()方法

上周,為了實(shí)現(xiàn)在代碼中動(dòng)態(tài)移動(dòng)View的位置的需求吁峻,遇到難以處理的問(wèn)題。

先看看需求

在XML布局文件中在张,擺放好View的位置用含。接下來(lái)以此布局位置播放動(dòng)畫,當(dāng)播放到一個(gè)結(jié)點(diǎn)的時(shí)候帮匾,要讓一個(gè)ImageView啄骇、兩個(gè)TextView出現(xiàn)在另外一個(gè)位置上,而且會(huì)有一些變化瘟斜。除了基本的XY的左邊變化了之外缸夹,ICON的圖標(biāo)的大小,TextView的TextSize螺句、TextColor虽惭、Lines、maxWidth都產(chǎn)生了變化蛇尚。本來(lái)用兩個(gè)View芽唇,分別布局在對(duì)應(yīng)的位置上,控制他們的Visibility就可以取劫,但因?yàn)橐恍┨厥庑源殷裕荒苣敲醋鲅新拢荒茏孷iew在播放動(dòng)畫的同時(shí)進(jìn)行View位置的移動(dòng)。

解決方案

在布局文件中擺放好不顯示的替身View炮捧,然后當(dāng)變化的時(shí)候庶诡,將替身的XY值傳遞給要變化的View。
理論上是可以實(shí)現(xiàn)的咆课,代碼如下:


image.png

實(shí)際上確實(shí)困難重重末誓。大小可以變,但是位置就是不能對(duì)上傀蚌。

最后總結(jié)出問(wèn)題出在requestLayout()上基显。

淺析requestLayout()

1.requestLayout()和invalidate()的區(qū)別

requestLayout():view調(diào)用這個(gè)方法要求parent view重新進(jìn)行一次測(cè)量、布局善炫、繪制這三個(gè)流程來(lái)更新自己位置撩幽。
invalidate():view調(diào)用這個(gè)方法迫使view進(jìn)行重新繪制。
一句話箩艺,requestLayout()的效果是重新布局自己在父布局中的位置窜醉,invalidate()的效果是強(qiáng)制調(diào)用自己的onDraw()方法。

2.分析requestLayout()

我們先看View的requestLayout()方法的源碼:

image.png

在requestLayout方法中艺谆,首先先為當(dāng)前View設(shè)置上PFLAG_FORCE_LAYOUT的標(biāo)記位榨惰,表示當(dāng)前的View需要重新繪制。

接下來(lái)會(huì)判斷當(dāng)前View樹是否正在布局流程静汤,這會(huì)調(diào)用ViewRootImpl的isLayoutRequested()方法琅催,如下:

image.png

當(dāng)父布局已經(jīng)開始重新布局的時(shí)候,不會(huì)繼續(xù)傳遞重新布局的請(qǐng)求虫给,而是帶著FORCE_LAYOUT的標(biāo)記等待重新繪制的流程走到這里藤抡。

當(dāng)并沒(méi)有已經(jīng)在重新布局的時(shí)候,接著調(diào)用mParent.requestLayout方法抹估,為父容器添加PFLAG_FORCE_LAYOUT標(biāo)記位缠黍,而父容器又會(huì)調(diào)用它的父容器的requestLayout方法.
requestLayout的事件會(huì)層層上傳,直到DecorView药蜻,即根View瓷式,而根View又會(huì)傳遞給ViewRootImpl。
即是說(shuō)任何一個(gè)View的requestLayout事件语泽,最終會(huì)被ViewRootImpl接收并得到處理贸典。

接下來(lái)看一下ViewRootImpl的requestLayout方法:


image.png
image.png

在這里,調(diào)用了scheduleTraversals方法踱卵,這個(gè)方法是一個(gè)異步方法瓤漏,post了一個(gè)Runnable,我們繼續(xù)跟進(jìn),看一下這個(gè)TraversalRunnable接口蔬充。

image.png

這個(gè)Runnable只調(diào)用了一個(gè)方法,doTraversal()班利,如下:


image.png

image.png

最終我們看到饥漫,會(huì)調(diào)用到performTraversals方法,這也是View工作流程的核心方法罗标。這個(gè)方法從1282行庸队,一直到2082行,一共800行代碼闯割,太長(zhǎng)了彻消,就不展示了。

這個(gè)方法有多重要呢宙拉?

整個(gè)Android的UI繪制機(jī)制是從哪里開始的即入口在哪里呢宾尚?答案就是ViewRootImpl類的performTraversals()方法。在這個(gè)方法內(nèi)部谢澈,分別調(diào)用measure煌贴、layout、draw方法來(lái)進(jìn)行View的三大工作流程锥忿。

至此牛郑,我們就能明白了,requestLayout()會(huì)牽動(dòng)出整個(gè)Android繪制機(jī)制重新走一遍流程敬鬓。

接下來(lái)淹朋,繼續(xù)看一下View的measure()方法:

image.png

image.png

首先是判斷一下標(biāo)記位,如果當(dāng)前View的標(biāo)記位為PFLAG_FORCE_LAYOUT钉答,那么就會(huì)進(jìn)行測(cè)量流程础芍,調(diào)用onMeasure,對(duì)該View進(jìn)行測(cè)量希痴,接著最后為標(biāo)記位設(shè)置為PFLAG_LAYOUT_REQUIRED,這個(gè)標(biāo)記位的作用就是在View的layout流程中者甲,如果當(dāng)前View設(shè)置了該標(biāo)記位,則會(huì)進(jìn)行布局流程砌创。

image.png

image.png

到目前為止虏缸,requestLayout的流程便完成了。

隨著流程的梳理完嫩实,導(dǎo)致我們出問(wèn)題的原因已經(jīng)找到刽辙,再看一眼出問(wèn)題的代碼:


image.png

我們先看一下,TextView的setTextSize方法甲献,


image.png

我們會(huì)發(fā)現(xiàn)宰缤,其中調(diào)用了requestLayout()方法和invalidate()方法。(requestLayout()方法的目的是重新調(diào)整TextView的擺放位置,invalidate()方法的目的是重新繪制一遍文字內(nèi)容)
當(dāng)這里已經(jīng)調(diào)用過(guò)requestLayout()方法的時(shí)候慨灭,由于ViewRootImpl對(duì)象是唯一的朦乏,所以其他的requestLayout()方法的調(diào)用都會(huì)被擱置等待。而這個(gè)方法又是異步的氧骤,所以如果我們同步的去變更調(diào)整TextView的位置呻疹,都是根據(jù)的舊布局取到的XY坐標(biāo)。所以解決方法如下:
image.png

不需要主動(dòng)調(diào)用requestLayout()方法了筹陵,在同步的變化所有的View的屬性之后刽锤,拋一個(gè)消息等待requestLayout()結(jié)束之后,再賦值朦佩,就可以在新布局的基礎(chǔ)上變化了并思,于是就按照預(yù)期的產(chǎn)生位置變動(dòng)了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末语稠,一起剝皮案震驚了整個(gè)濱河市宋彼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌颅筋,老刑警劉巖宙暇,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異议泵,居然都是意外死亡占贫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門先口,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)型奥,“玉大人,你說(shuō)我怎么就攤上這事碉京∠嵝冢” “怎么了?”我有些...
    開封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵谐宙,是天一觀的道長(zhǎng)烫葬。 經(jīng)常有香客問(wèn)我,道長(zhǎng)凡蜻,這世上最難降的妖魔是什么搭综? 我笑而不...
    開封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮划栓,結(jié)果婚禮上兑巾,老公的妹妹穿的比我還像新娘。我一直安慰自己忠荞,他們只是感情好蒋歌,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開白布帅掘。 她就那樣靜靜地躺著,像睡著了一般堂油。 火紅的嫁衣襯著肌膚如雪修档。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天称诗,我揣著相機(jī)與錄音萍悴,去河邊找鬼。 笑死寓免,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的计维。 我是一名探鬼主播袜香,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鲫惶!你這毒婦竟也來(lái)了蜈首?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤欠母,失蹤者是張志新(化名)和其女友劉穎欢策,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赏淌,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡踩寇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了六水。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俺孙。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖掷贾,靈堂內(nèi)的尸體忽然破棺而出睛榄,到底是詐尸還是另有隱情,我是刑警寧澤想帅,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布场靴,位于F島的核電站,受9級(jí)特大地震影響港准,放射性物質(zhì)發(fā)生泄漏旨剥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一叉趣、第九天 我趴在偏房一處隱蔽的房頂上張望泞边。 院中可真熱鬧,春花似錦疗杉、人聲如沸阵谚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)梢什。三九已至奠蹬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嗡午,已是汗流浹背囤躁。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荔睹,地道東北人狸演。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像僻他,于是被迫代替她去往敵國(guó)和親宵距。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361