大綱
- 使用dp而不是px
- 盡量使用自動(dòng)適配布局,而不要指定分辨率
- 使用寬高限定符
- values-1080x1920匈挖,以1080P為基準(zhǔn)計(jì)算每種常見分辨率對(duì)應(yīng)的尺寸儡循。
- 需要盡可能全的添加各種設(shè)備的分辨率(有工具)
- 容錯(cuò)性不足择膝,如果設(shè)備分辨率不能精確匹配對(duì)應(yīng)限定符肴捉,會(huì)默認(rèn)使用統(tǒng)一默認(rèn)的dimens
- 第三方自動(dòng)適配UI框架
- 原理:自定義RelativeLayout齿穗,在onMeasure中對(duì)控件分辨率做變換
- 第三方框架饺律,維護(hù)性很成問題
- 一些自定義View蓝晒,處理比較麻煩
- 最小寬度限定符芝薇,類似寬高限定符
- values-sw240dp洛二,同樣以某一dp寬度為基準(zhǔn)計(jì)算其他寬度dp的值
- values-sw360dp晾嘶、values-sw480dp
- 相比寬高限定符垒迂,最小寬度限定符不進(jìn)行精確匹配妒蛇,會(huì)遵循就近原則楷拳,可以較好的解決容錯(cuò)問題欢揖。
- 如:設(shè)備寬364dp她混,系統(tǒng)會(huì)自動(dòng)就近配置values-sw360dp下的dimens泊碑,顯示效果相差不會(huì)很大
- 今日頭條——修改density值
- 原理:px = dp x (dpi/160) = dp x density
- 既然如此蛾狗,將density
- 需要UI出設(shè)計(jì)圖時(shí)以統(tǒng)一的dp為基準(zhǔn)
- https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA
基本概念
- 像素——px
- 密度獨(dú)立像素——dp或dip
- 像素密度——dpi沉桌,單位面積內(nèi)的像素?cái)?shù)。
- 軟件系統(tǒng)的概念佃扼。
- 在系統(tǒng)出廠時(shí)兼耀,配置文件中的固定值瘤运。
- 通常的取值有:160匠题、240韭山、360钱磅、480等。
- 不同于物理概念上的屏幕密度ppi年柠,如ppi為415彪杉、430和470時(shí)派近,dpi可能會(huì)統(tǒng)一設(shè)置為480。
- density——當(dāng)dpi=160時(shí)侯嘀,1px = 1pd,此時(shí)denstiy的值為1戒幔,dpi=240時(shí)土童,1.5px = 1dp,density的值為1.5献汗。
- 上述值的關(guān)系:
- denstiy = dpi / 160;
- px = dp x density = dp x (dpi / 160)
Android設(shè)備的碎片化極為嚴(yán)重楚午,各種尺寸和分辨率的設(shè)備無比繁多矾柜。使得在Android開發(fā)中就谜,UI適配變成了開發(fā)過程中極為重要的一步丧荐。為此Google提出了密度獨(dú)立像素dip或dp的概率,旨在更友好的處理Android UI適配問題。
但是效果嘛窟却,只能說差強(qiáng)人意夸赫,可以解決大部分的業(yè)務(wù)場(chǎng)景咖城,但是剩下的個(gè)別情況就搞死人了呼奢,原因在于Android設(shè)備碎片化實(shí)在太嚴(yán)重了握础,存在各種分辨率和dpi的設(shè)備禀综。
比如兩臺(tái)設(shè)備A和B定枷,分辨率是1920x1080欠窒,dpi分別為420和480岖妄,在布局中編寫一個(gè)100dp寬的ImageView衣吠,按照上面的公式ImageView的顯示寬度分別為:100dp x 420 / 160 = 262.5
和100dp x 480 / 160 = 300
壤靶,ImageView在B設(shè)備上明顯顯示要大一些忧换。差異可能還不明顯向拆,我們把寬度改為360dp呢浓恳,A設(shè)備顯示寬度為:948px梢夯,B設(shè)備顯示寬度為:1080px颂砸。這就扯淡了,一個(gè)寬度填充滿屏幕勤篮,一個(gè)不滿碰缔。這種情況肯定是需要開發(fā)來背鍋解決的手负。
適配方案
雖然上面提到了使用dp無法解決全部業(yè)務(wù)場(chǎng)景姑尺,但是相對(duì)于直接使用px已經(jīng)可以解決大部分場(chǎng)景下的適配問題了竟终。
所以UI適配的第一條就是:
1. 使用dp代替px來編寫布局。
又因?yàn)樯厦鏌o法適配的個(gè)別場(chǎng)景切蟋,所以UI適配的第二條是:
2.盡量使用自動(dòng)適配布局统捶,而不要指定分辨率
這一條也很好理解,盡量使用ConstraintLayout
約束布局和LinearLayout
等父布局柄粹,不要寫死分辨率喘鸟,比如上面的例子如果使用match_parent
而不是360dp
,也可以避免出現(xiàn)顯示不一致問題(但是僅限于上列)驻右。
限定符
Google同樣意識(shí)到dp滿足所以業(yè)務(wù)場(chǎng)景的需要什黑,所以提供了寬度限定符的概念堪夭。
雖然您的布局應(yīng)始終通過拉伸其視圖內(nèi)部和周圍的空間來應(yīng)對(duì)不同的屏幕尺寸,但這可能無法針對(duì)每種屏幕尺寸提供最佳用戶體驗(yàn)橘蜜。例如,您為手機(jī)設(shè)計(jì)的界面或許無法在平板電腦上提供良好的體驗(yàn)。因此,您的應(yīng)用還應(yīng)提供備用布局資源克蚂,以針對(duì)特定屏幕尺寸優(yōu)化界面設(shè)計(jì)悉罕。
最小寬度限定符
使用“最小寬度”屏幕尺寸限定符类早,您可以為具有最小寬度(以密度無關(guān)像素 dp 或 dip 為度量單位)的屏幕提供備用布局逆日。
通過將屏幕尺寸描述為密度無關(guān)像素的度量值,Android 允許您創(chuàng)建專為非常具體的屏幕尺寸而設(shè)計(jì)的布局坪圾,同時(shí)讓您不必對(duì)不同的像素密度有任何擔(dān)心已日。
通俗一點(diǎn)翻譯就是:可用通過xxxx-swXXXdp的方式定義一些最小限定符的資源文件,比如:values-sw400dp护奈、values-sw600dp,系統(tǒng)會(huì)自動(dòng)匹配如屏幕寬度相近資源文件夾。
我們?cè)賮砜瓷厦娴睦觾膳_(tái)設(shè)備A和B檐晕,分辨率是1920x1080篡石,dpi分別為360和400。我們簡(jiǎn)化下問題比如設(shè)計(jì)圖給的是1920x1080 360dpi湿诊,包含一個(gè)22.5px * 22.5px = 10dp * 10dp的圖片朗和。按經(jīng)驗(yàn)布局應(yīng)該如下編寫:
<ImageView
android:id="@+id/img_iv"
android:layout_width="10dp"
android:layout_height="10dp"
android:background="@mipmap/ic_launcher"/>
在不同設(shè)備上運(yùn)行的結(jié)果:
- 1280 x 720 240dpi的設(shè)備憔儿,圖片顯示為15px * 15px;
- 1920 x1080 360dpi的A設(shè)備拾氓,圖片顯示為22.5px * 22.5px;
- 1920 x1080 400dpi的B設(shè)備,圖片顯示為25px * 25px;
可以看到B設(shè)備圖片顯示是有問題的,為了解決這個(gè)問題拒课,我們使用最小寬度限定符定義兩個(gè)資源文件夾:values-sw360dp和values-sw400dp。
在values-sw360dp中添加dimen.xml內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="dp_1">1dp</dimen>
<dimen name="dp_2">2dp</dimen>
<dimen name="dp_3">3dp</dimen>
<dimen name="dp_4">4dp</dimen>
<dimen name="dp_5">5dp</dimen>
<dimen name="dp_6">6dp</dimen>
<dimen name="dp_7">7dp</dimen>
<!-- 省略其他值 -->
<dimen name="dp_360">360dp</dimen>
<!-- 因?yàn)樵O(shè)計(jì)圖是360dpi,所以控件尺寸通常不會(huì)超過360dp,定義最大360dp的值足夠使用 -->
</resources>
在values-sw420dp中添加dimen.xml,文件中的dimen值很容易換算出來:在360dpi中dp_1 = 1dp,那么在400dpi中dp_1 = 360 / 400 = 0.9dp,文件內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="dp_1">0.9dp</dimen>
<dimen name="dp_2">1.8dp</dimen>
<dimen name="dp_3">2.7dp</dimen>
<dimen name="dp_4">3.6dp</dimen>
<dimen name="dp_5">4.5dp</dimen>
<dimen name="dp_6">5.4dp</dimen>
<dimen name="dp_7">6.3dp</dimen>
<!-- 省略其他值 -->
</resources>
注意要在values文件夾下添加默認(rèn)dimen.xml沼撕,文件內(nèi)容與values-sw360dp中添加dimen.xml一致(因?yàn)樵O(shè)計(jì)圖恰好是360dpi的)笼沥。
布局中的ImageView自然要改寫為:
<ImageView
android:id="@+id/img_iv"
android:layout_width="@dimen/dp_10"
android:layout_height="@dimen/dp_10"
android:background="@mipmap/ic_launcher"/>
我們?cè)賮砜匆幌虏煌O(shè)備運(yùn)行結(jié)果:
- 1280 x 720 240dpi的設(shè)備,未匹配到限定符使用values中的dimen鉴裹,
dp_10 = 10dp, px = 10 * 240 / 160 = 15px
总处,圖片顯示尺寸為15px * 15px荸频。 - 1920 x1080 360dpi的A設(shè)備遇绞,匹配到sw360dp限定符褐健,
dp_10 = 10dp, px = 10 * 360 / 160 = 20px
,圖片顯示尺寸為22.5px * 22.5px坐梯。 - 1920 x1080 400dpi的B設(shè)備蹋辅,匹配到sw420dp限定符褒傅,
dp_10 = 9dp, px = 9 * 400 / 160 = 20px
碌尔,圖片顯示尺寸為22.5px * 22.5px叹坦。
完美的解決了設(shè)備A和B的顯示問題测蹲,所以UI適配的第三條是:
3. 使用最辛鹜凇(可用)寬度限定符,解決同樣分辨率不同dpi的設(shè)備適配問題妙同。
這種方案看似完美芒涡,但是也有一些隱含的問題:此方案只能解決同樣分辨率不同dpi設(shè)備的適配問題:
- 一旦出現(xiàn)不同分辨率相同dpi的情況就無效了(當(dāng)然這種情況的可能性不高)旱幼。
- 以上舉例只是基于1920x1080這一種分辨率為例說明,試想一下如果1280x720的設(shè)備存在240dpi和280dpi的情況呢弛车?我們只能針對(duì)特殊情況適配處理,無法解決全部場(chǎng)景適配問題。
寬高限定符
類似于上面說的最小寬度限定符浩姥,但是需要精確指定要匹配的設(shè)備寬高兜挨,values-1920x1080柒桑、values-1280x720等。配置與使用方式也與上面類似净响,如設(shè)計(jì)圖尺寸為1920x1080 360dpi,那么只需要以1920x1080為基準(zhǔn)計(jì)算所有分辨率對(duì)應(yīng)的尺寸就可以了刁卜,布局編寫時(shí)按照給的尺寸一一對(duì)應(yīng)就可以蛔趴,比如:給出的ImageView是20px*20px的,那在布局中同樣指定width和height為@dimen/dp_20
就可以了例朱。
values-1920x1080中dimens.xml如下:
<resources>
<dimen name="dp_1">1px</dimen>
<dimen name="dp_2">2px</dimen>
<dimen name="dp_3">3px</dimen>
<dimen name="dp_4">4px</dimen>
<dimen name="dp_5">5px</dimen>
<dimen name="dp_6">6px</dimen>
<dimen name="dp_7">7px</dimen>
<dimen name="dp_8">8px</dimen>
<dimen name="dp_9">9px</dimen>
<!-- 省略其他 -->
<dimen name="dp_1920">1920px</dimen>
</resources>
values-1280x720中dimens.xml換算為:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="dp_1">0.66px</dimen>
<dimen name="dp_2">1.33px</dimen>
<dimen name="dp_3">2.0px</dimen>
<dimen name="dp_4">2.66px</dimen>
<dimen name="dp_5">3.33px</dimen>
<dimen name="dp_6">4.0px</dimen>
<dimen name="dp_7">4.66px</dimen>
<!-- 省略其他 -->
</resources>
同樣需要在values添加默認(rèn)尺寸dimen.xml孝情,內(nèi)容同基準(zhǔn)分辨率文件鱼蝉。
因?yàn)椴皇撬性O(shè)備屏幕都是16:9的,也可以按照寬高拆分成兩個(gè)dimens.xml文件dimen_x.xml和dimen_y.xml箫荡,按照寬高:1920x1080分別換算得到x和y的值魁亦,但是頁面設(shè)計(jì)通常是豎屏可滑動(dòng)的,所以對(duì)高度不敏感菲茬,只需要根據(jù)一個(gè)維度計(jì)算統(tǒng)一值就可以了吉挣。
以上計(jì)算方式比較簡(jiǎn)單了,不需要自己編寫換算可以通過代碼工具或者自己寫個(gè)類實(shí)現(xiàn)婉弹。(網(wǎng)上有好多睬魂,找一下應(yīng)該可以找到)。
理論上只要盡可能多的枚舉所有設(shè)備分辨率镀赌,就可以完美的解決屏幕適配問題氯哮,所以UI適配的第四條是:
4.使用寬高限定符,精確匹配屏幕分辨率商佛。
這種方案已經(jīng)近乎完美了喉钢,一度成為比較熱門的解決方案,也有很多團(tuán)隊(duì)使用過此方案良姆。但是之前也說過Android設(shè)備的碎片化太嚴(yán)重了肠虽,綜合考慮基本不可能在項(xiàng)目中枚舉所有的屏幕尺寸進(jìn)行適配,如果設(shè)備沒有匹配到對(duì)應(yīng)尺寸會(huì)使用values下的默認(rèn)尺寸文件玛追,可能會(huì)出現(xiàn)嚴(yán)重的UI適配問題税课。
但是不可否認(rèn)此種方案實(shí)現(xiàn)簡(jiǎn)單,對(duì)于編寫布局也很友好(直接填入設(shè)計(jì)圖的尺寸值就行痊剖,不需要換算)韩玩,可以解決絕大多數(shù)的設(shè)備適配問題,是一種很友好的解決方案陆馁。
第三方UI適配框架
有很多第三方庫的解決方案找颓,是從ViewGroup入手的,要么重寫常用的如:RelativeLayout叮贩、LinearLayout和FrameLayout等在控件內(nèi)部做轉(zhuǎn)換來適配不同尺寸的設(shè)備击狮,要么提供新的Layout如:Google的PercentLayout布局。但是這些方案基本都不在維護(hù)了益老,這里就不詳細(xì)展開了帘不,感興趣的可以自行搜索了解。
UI適配的第五條是:
5. 使用第三方自適配框架杨箭,解決UI適配問題寞焙。
感興趣的可以參考以下文檔:
其他適配方案
參考字節(jié)的實(shí)現(xiàn)方案:
這篇文章著實(shí)屬于拾人牙慧了,起因是因?yàn)榭吹搅诉@篇博客Android 目前最穩(wěn)定和高效的UI適配方案。所以想著確實(shí)應(yīng)該把這部分知識(shí)梳理一下捣郊,所以寫了這篇文檔加了一些自己的里面辽狈,主要也是為了梳理知識(shí)點(diǎn)加深理解。
文中列舉的幾種UI適配方案呛牲,沒有嚴(yán)格的優(yōu)劣之分刮萌,可以根據(jù)自己的業(yè)務(wù)需求選擇,也可以選擇幾種搭配使用娘扩,比如筆者目前主要做智能電視(盒子)的應(yīng)用開發(fā)着茸,Android電視不同于手機(jī),碎片化沒有那么嚴(yán)重琐旁,電視分辨率種類屈指可數(shù)涮阔,所以在日常項(xiàng)目中基本選擇使用寬高限定符的方案進(jìn)行適配,效果也是極好的灰殴。