源碼分析Dialog自定義大小無(wú)效坑

最近在項(xiàng)目中用到了自定義Dialog,以前也是經(jīng)常用,只不過(guò)要么是用自帶的dialog樣式,要么也是很簡(jiǎn)單的布局,所以并沒(méi)有重視修改dialog大小的坑。直到這次項(xiàng)目中產(chǎn)(keng)品(die)經(jīng)(wan)理(yi)死了都說(shuō)dialog大小別扭要求改,然后突然發(fā)現(xiàn)自己自定義的dialog的大小不能調(diào)整后整個(gè)人都驚呆了,因此打算深入源碼看看解決這個(gè)問(wèn)題。本文分析的源代碼均來(lái)自Android API 24。


demo的自定義布局如下:

首先想著通過(guò)修改dialog的Window來(lái)進(jìn)行修改,代碼如下:


開開心心運(yùn)行一下:

em.....emmmm???尼瑪?這啥玩意兒??還是沒(méi)變?再怎么4000的大小也不會(huì)是這么個(gè)小玩意兒把?郁悶之極....(我太仁慈大小給4000太小了?)

又繼續(xù)試驗(yàn),不斷的調(diào)整各種大小,還是沒(méi)效果!最后求助網(wǎng)絡(luò)后發(fā)現(xiàn),設(shè)置窗口大小的代碼必須放在.show()之后才能有效果,代碼修改如下:
運(yùn)行后查看效果:
成功設(shè)置了大??!開心得不得了?。?!

然而百思不得其解為什么必須要在show之后才能顯示??按照正常邏輯不應(yīng)該在show之前設(shè)置大?。???

在網(wǎng)上找半天也沒(méi)找到前人們分析源碼,所以決定自己硬著頭皮看看源碼,點(diǎn)進(jìn)show()方法后進(jìn)行,一看大有文章,先上代碼:

show方法中的代碼居然不是我想象中的一兩行代碼,而是那么多!首先在上圖中標(biāo)注1處,因?yàn)槲覀兪莿?chuàng)建新的dialog,所以會(huì)執(zhí)行dispatchOnCreate(null)這個(gè)方法。顧名思義,調(diào)用創(chuàng)建,這里面肯定大有文章,猜測(cè)是進(jìn)行了Dialog的創(chuàng)建,我們跟進(jìn)去查看具體源碼。

跟進(jìn)去后發(fā)現(xiàn)里面調(diào)用了onCreate()方法,在這里是不是有種似曾相識(shí)的感覺(jué)?怎么感覺(jué)和Activity差不多了呢?按捺住基動(dòng)的內(nèi)心我們跟進(jìn)去繼續(xù)看看。因?yàn)镈ialog的onCreate()方法實(shí)現(xiàn)為空的,所以我們選擇一個(gè)子類來(lái)進(jìn)行查看,我們進(jìn)入AlertDialog中查看:



我們發(fā)現(xiàn)調(diào)用了mAlet.installContent()方法,我們來(lái)看看這是什么東東。AlertController是一個(gè)AlertDialog的控制類,包括在創(chuàng)建AlertDialog時(shí)(代碼如下):
AlertDialog mTextDialog = new AlertDialog.Builder(context).setTitle("溫馨提示").setView(view) .create();

在這里使用的Builder模式中,setTitle之類的設(shè)置參數(shù)也是先將參數(shù)設(shè)置給AlertController.Param。具體在這里就暫時(shí)不管。
我們繼續(xù)查看installContent()方法:


先設(shè)置dialog的contentView,然后調(diào)用setContentView()方法。是不是越來(lái)越熟悉這個(gè)套路!繼續(xù)忍著壓抑砰砰直跳的小內(nèi)心,繼續(xù)跟進(jìn)去看看。

我們看到,在Dialog類中我們發(fā)現(xiàn)它最終調(diào)用了Window的setContentView()方法;而Window在Android中只有PhoneWindow這一個(gè)實(shí)現(xiàn)類。 接下來(lái)的工作就是和Activity中的步驟一模一樣,因?yàn)槎际钦{(diào)用Window的setContentView()方法;具體分析可看我記錄的另一篇文章內(nèi)容(傳送門:http://www.itdecent.cn/p/28bbb6778593)。
通過(guò)setContentView()方法后,創(chuàng)建了Dialog的decorView,并且將我們的自定義布局加入到decorView中。其中在這個(gè)過(guò)程中,mWindowAttributes的高度和寬度在mWindow.setContentView()中的installDecor()中的generateLayout()中被修改,在這里貼出修改部分的代碼:
修改窗口寬高調(diào)用代碼

setLayout()具體代碼

在上面圖中的標(biāo)注處調(diào)用了setLayout(int width,int height)方法,這里傳入了自適應(yīng)的常量方式。而在setLayout方法中首先獲取window的LayoutParams,然后修改了寬高。因此在這里過(guò)后,Window的mWindowAttributes將會(huì)重新改變,因此導(dǎo)致了我們?cè)趕how之前修改的mWindowAttributes值將被覆蓋,因此失效!
接下來(lái)把debug模式下錯(cuò)誤代碼的參數(shù)貼出來(lái)看看:

如下圖為debug下show之前設(shè)置WindowManager.LayoutParams的值,可以看到我們把window的寬高設(shè)置為1143.
下圖為show方法內(nèi)執(zhí)行過(guò)了dispatchOnCreate()方法后的WindowManager.LayoutParams的值.

可以看出就算之前設(shè)置參數(shù),隨后在show方法中也會(huì)被覆蓋,因此在show之前設(shè)置參數(shù)無(wú)效。

所以我們因此只需要在show()之后設(shè)置即可。這樣就可以設(shè)置Dialog布局大小。而調(diào)用dialog.getWindow().setAttributes(params);這句話中,會(huì)觸發(fā)Window參數(shù)變化的接口,從而使得dialog重新設(shè)置自身大小。代碼如下:

至此我們就將設(shè)置Dialog的Window寬高沒(méi)有效果的原因分析完畢。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容