? ? ? 前段時(shí)間hongyang大神發(fā)布了一個(gè)庫(kù)暂幼,AndroidAutoLayout藐窄。該庫(kù)的使用贮泞,是用戶(該庫(kù)的使用者递瑰,即,猿們)告訴app,設(shè)計(jì)圖的寬和高為多少像素隙畜,然后在UI布局里直接使用px作為單位抖部,該庫(kù)會(huì)自動(dòng)將填寫的px值轉(zhuǎn)換為屏幕的百分比值,以此來(lái)完成適配议惰。
? ? 該庫(kù)的使用方式有兩種慎颗,一種是直接將Activity extends AutoLayoutActivity,另一種是在布局文件里使用該庫(kù)提供的三個(gè)代替LinearLayout, FrameLayout, RelativeLayout的AutoLinearLayout, AutoFrameLayout, AutoRelativeLayout控件.
? ? ?如果是使用直接繼承AutoLayoutActivity的方法言询,這個(gè)相當(dāng)省事俯萎。其實(shí)現(xiàn)是:在AutoLayoutActivity類內(nèi)部重寫了onCreateView(),這個(gè)方法會(huì)在Activity的onCreate()之后調(diào)用运杭,onCreateView調(diào)用完畢才調(diào)用Activity生命周期函數(shù)onStart().
? ? ? 這是它在Activity源碼中的說(shuō)明夫啊,默認(rèn)實(shí)現(xiàn)是返回null,它是LayoutInflater.Factory#onCreateView()的實(shí)現(xiàn)辆憔。
? ? ? ?而Factory是LayoutInflater中的一個(gè)接口
? ? ?在這個(gè)方法傳進(jìn)來(lái)的參數(shù)中的name是每一個(gè)View(放在布局文件中的View)的名字撇眯,比如LinearLayout, FrameLayout,TextView。
? ? ?在Activity中onCreateView()會(huì)被調(diào)用多次虱咧,順序是LinearLayout , ViewStub ,FrameLayout(這個(gè)就是ContentFrameLayout)熊榛,之后就是我們寫在xml里的View了,所以在這里我們能拿到寫在xml文件里的控件名字腕巡。
? ? ?LayoutInflater創(chuàng)建View的方法是createView()玄坦,其流程是:從xml中解析出來(lái)信息后,判斷是否帶包名绘沉,如果沒(méi)帶包名就給name拼接上前綴(也就是系統(tǒng)的包名煎楣,例如TextView變成android.widget.TextView),帶包名的(使用自定義View车伞,擴(kuò)展包View的時(shí)候要帶包名)就不需要加前綴择懂。拿到了這個(gè)View的包名+類名,類加載器加載class帖世,取得構(gòu)造方法休蟹,將View new出來(lái)沸枯。
? ? 而如果onCreateView返回的不是null,而是一個(gè)View赂弓,就不會(huì)走createView()绑榴。
? ? 作者就是在AutoLayoutActivity中重寫了該方法,并判斷如果是LinearLayout盈魁,就返回該庫(kù)里的AutoLinearLayout翔怎。RelativeLayout,F(xiàn)rameLayout同上杨耙。如此便做到了偷梁換柱的效果赤套,我們?cè)趚ml里使用的是LinearLayout,但實(shí)際生成的AutoLinearLayout珊膜。
? ? ? 而用戶不是選擇使用Activity extends AutoLayoutActivity這種方式容握,而是在xml文件里直接寫上包名+AutoXXXLayout,額车柠。剔氏。。這里好像沒(méi)什么好說(shuō)的竹祷。
? ? ? 接下來(lái)谈跛,我們來(lái)看該庫(kù)的自動(dòng)布局類,這里分析AutoFrameLayout塑陵,其余兩個(gè)類似感憾。
? ? ? 先說(shuō)下背景,由于我們已經(jīng)告訴app我們?cè)O(shè)計(jì)圖的大小了(該庫(kù)的使用條件之一令花,在Manifest.xml中添加meta阻桅,兩個(gè)字段design_width,design_height),程序就能拿到設(shè)計(jì)圖的寬高(單位為像素)彭则,而設(shè)備的屏幕寬高我們也是可以拿到的鳍刷。當(dāng)我們?cè)诓季治募飳iew的屬性值設(shè)為像素值時(shí)占遥,自然就可以通過(guò)換算得到實(shí)際上在不同的屏幕上該占多少百分比的像素了俯抖。以寬為例:
(edit_width / design_width)* screen_width 這個(gè)結(jié)果就是控件實(shí)際上的像素值。edit_width是用戶填寫的px值瓦胎。這部分代碼就不分析了芬萍。
? ? ? 既然已經(jīng)把FrameLayout偷梁換柱成我們自己的View(AutoFrameLayout)了,那么就可以拿到布局文件時(shí)寫的各個(gè)值(包括自定義屬性搔啊,系統(tǒng)屬性)柬祠。
作者在AutoFrameLayout中主要的代碼是:
重寫generateLayoutParams(),讓該ViewGroup中的每一個(gè)View的LayoutParams變成該類的靜態(tài)內(nèi)部類LayoutParams负芋。
而在onMeasure()方法中借由AutoLayoutHelper來(lái)調(diào)整每一個(gè)子View的屬性(該庫(kù)支持9個(gè)屬性)漫蛔。
? ? ?而在AutoFrameLayout.LayoutParams類中會(huì)把該庫(kù)現(xiàn)有兩個(gè)自定義屬性(basewidth,baseheight),和9個(gè)支持的系統(tǒng)屬性的值拿出來(lái),然后將2個(gè)自定義屬性和9個(gè)屬性值放在AutoFrameLayout.LayoutParams的成員變量AutoLayoutInfo中莽龟,并提供getter蠕嫁。
? ? ? 這里有一點(diǎn)需要提一下,在記錄過(guò)程中毯盈,只有屬性是以“px”結(jié)尾的屬性才會(huì)被記錄下來(lái)剃毒。也就是說(shuō),如果你在AutoFrameLayout中某個(gè)View某個(gè)屬性不想使用這個(gè)百分比自動(dòng)布局的功能搂赋,只要不把屬性寫成px的就行了赘阀。(我在用這個(gè)庫(kù)的時(shí)候,有一個(gè)View的寬屬性無(wú)法事前確定脑奠,只能wrap_content基公。還在想該庫(kù)是不是可以提供一個(gè)自定義屬性ignore可以讓該View免于自動(dòng)布局,看了代碼才知道作者早就設(shè)計(jì)好了)
下面是adjustChildren()的代碼:
? ? ? 作者先做了一次檢查宋欺,checkParams()酌媒,如果用戶沒(méi)有在Manifest.xml中提供design_width,design_height會(huì)報(bào)錯(cuò)迄靠。
? ? ?之后就是拿到mHost(AutoXXXLayout)中的每一個(gè)View的LayoutParams秒咨,取出之前記錄了自定義屬性和屬性值的AutoLayoutInfo,然后將里面的每一個(gè)屬性作用到View上掌挚。這樣就完成了View屬性的更改雨席。
? ? ?該庫(kù)提供兩個(gè)自定義屬性layout_auto_basewidth, layout_auto_baseheight。由于手機(jī)屏幕的碎片化(Android手機(jī)你懂的)吠式,比如想讓一個(gè)ImageView的寬高一致時(shí)陡厘,寫寬20px,高20px特占,經(jīng)過(guò)百分比換算屏幕大小糙置,很可能就不一致了。所以需要一個(gè)單位基準(zhǔn)是目,比如寬20px谤饭,高20px,layout_auto_basewidth=height懊纳,那么高度就會(huì)以width為基準(zhǔn)了揉抵,其換算公式是(edit_height/design_width) * screen_width。如此嗤疯,這個(gè)ImageView的寬高就一樣大小了冤今,正方形。
? ? AutoAttr這個(gè)類茂缚,作者用來(lái)封裝每一個(gè)屬性值的信息戏罢,多少px屋谭,以哪個(gè)方向(寬,高)為基準(zhǔn)龟糕。如果我們?cè)趚ml里沒(méi)有指定某屬性的基準(zhǔn)戴而,就會(huì)使用該屬性的默認(rèn)基準(zhǔn)。默認(rèn)基準(zhǔn)是翩蘸,豎直方向上以高為基準(zhǔn)所意,水平方向以寬為基準(zhǔn)。即:marginTop以height為基準(zhǔn)催首,marginLeft以width為基準(zhǔn)扶踊,textSize以height為基準(zhǔn),其他的同上郎任。
? ? 最后秧耗,如果換算出來(lái)不足1px,以1px返回舶治。
AutoAttr的apply()方法代碼如下:
? ? 以上是我對(duì)AndroidAutoLayout的理解分井,如果錯(cuò)了,還請(qǐng)指正霉猛,謝謝尺锚。