適配緣由
做Android開(kāi)發(fā)一定會(huì)碰到適配這個(gè)問(wèn)題顶考,在Android世界里,Android設(shè)備太多了妖泄,手機(jī)驹沿,平板,TV蹈胡,手表等渊季,光其中的手機(jī)這一項(xiàng)就有眾多廠(chǎng)家發(fā)布的奇奇怪怪的手機(jī)朋蔫,不僅分辨率各有不同,就是手機(jī)尺寸也是一言難盡却汉,各種尺寸都有驯妄,更煩的是有的手機(jī)還在屏幕上搞個(gè)虛擬導(dǎo)航欄放在底部;廠(chǎng)家多也就算了合砂,由于Android系統(tǒng)的開(kāi)源青扔,任何廠(chǎng)家,個(gè)人翩伪,OEM廠(chǎng)商微猖,運(yùn)營(yíng)商都可以對(duì)Android進(jìn)行定制,修改成他們想要的樣子缘屹,這更加大了適配的難度(尤其是我在平時(shí)開(kāi)發(fā)中經(jīng)常會(huì)用到一些工程機(jī))凛剥;總之而言,Android的碎片化太嚴(yán)重了轻姿;不像ios適配機(jī)型有限犁珠,所以解決掉Android適配問(wèn)題是做一個(gè)好的APP的第一步
相關(guān)概念
在開(kāi)發(fā)過(guò)程中,UI給出的設(shè)計(jì)圖上的所有元素尺寸都是以px作為單位的互亮,但是我們?cè)趌ayout文件中定義相關(guān)View的長(zhǎng)寬是用dp作為單位來(lái)指定其值的盲憎,定義文字大小是用sp作為單位,還可能會(huì)涉及到屏幕尺寸胳挎,屏幕分辨率饼疙,屏幕像素密度dpi
這里說(shuō)到了四個(gè)單位:px dp sp dpi,還有屏幕尺寸及屏幕分辨率:
屏幕分辨率:指手機(jī)橫縱方向上的像素點(diǎn)數(shù)慕爬,比如常見(jiàn)的720 * 1280 窑眯,1080 * 1920,單位px医窿,1px=一個(gè)像素點(diǎn)磅甩,
屏幕尺寸:不是屏幕寬度,也不是長(zhǎng)度姥卢,而是屏幕的對(duì)角線(xiàn)長(zhǎng)度卷要,比如常見(jiàn)的5.0尺寸,5.5尺寸独榴,單位是英寸僧叉,1英寸=2.54厘米
dpi:全稱(chēng)dots per ich,也就是每英寸的像素點(diǎn)數(shù)(其實(shí)不是真正有多少像素點(diǎn)數(shù))棺榔,或者說(shuō)屏幕像素密度瓶堕;它是一個(gè)軟件上的概念,是在系統(tǒng)軟件上指定的單位尺寸的像素?cái)?shù)量症歇,它往往是寫(xiě)在系統(tǒng)出廠(chǎng)配置文件的一個(gè)固定值郎笆;我們可能還接觸過(guò)一個(gè)單位叫ppi谭梗,它也是像素密度,這個(gè)是物理上的概念宛蚓,它是客觀(guān)存在的不會(huì)改變激捏。dpi是軟件參考了物理像素密度后,人為指定的一個(gè)值凄吏,這樣保證了某一個(gè)區(qū)間內(nèi)的物理像素密度在軟件上都使用同一個(gè)值缩幸。這樣會(huì)有利于我們的UI適配;比如竞思,幾部相同分辨率不同尺寸的手機(jī)的ppi可能分別是是430,440,450,那么在Android系統(tǒng)中表谊,可能dpi會(huì)全部指定為480;ppi與屏幕尺寸和屏幕分辨率有關(guān)盖喷,它們的關(guān)系如下
px:這個(gè)大家容易理解爆办,也就是真實(shí)像素的單位,就跟s是時(shí)間秒的單位一樣
sp:全稱(chēng)scale-independent pixel课梳,也叫sip距辆,即獨(dú)立縮放像素,Android特有的用來(lái)描述文字大小的單位暮刃,建議使用12,14,18,20等偶數(shù)值跨算,避免奇數(shù)和小數(shù)造成精度的丟失問(wèn)題;為什么用這個(gè)不用dp呢椭懊?這是因?yàn)槲覀兛梢栽谑謾C(jī)系統(tǒng)設(shè)置中修改文字的大小诸蚕,如果使用dp單位,文字大小不會(huì)被改變氧猬;而使用sp會(huì)隨著修改而變化背犯;具體原因可參考sp與dp的區(qū)別
dp:全稱(chēng)Density-independent pixel 也叫dip,即獨(dú)立像素密度盅抚,它的一個(gè)特性就是與像素?zé)o關(guān)漠魏,或者說(shuō)與終端上的實(shí)際物理像素點(diǎn)無(wú)關(guān),是Android特有的用來(lái)描述View尺寸的單位妄均,保證同一個(gè)分辨率的圖片在不同分辨率的手機(jī)上保持同樣的視覺(jué)大兄隆(占用相識(shí)比例的空間)
比如同樣都是畫(huà)一條長(zhǎng)度是屏幕一半的線(xiàn),如果使用px作為計(jì)量單位丰包,那么在480x800分辨率手機(jī)上設(shè)置應(yīng)為240px禁熏;在320x480的手機(jī)上應(yīng)設(shè)置為160px,二者設(shè)置就不同了烫沙;如果使用dp為單位匹层,在這兩種分辨率下隙笆,160dp都顯示為屏幕一半的長(zhǎng)度锌蓄,原因在下方
在Android中升筏,規(guī)定以160dpi為基準(zhǔn),1dp=1px瘸爽;320dpi下您访,1dp = 2px,計(jì)算公式:px = dp * (dpi / 160)剪决,屏幕密度越大灵汪,1dp對(duì)應(yīng) 的像素點(diǎn)越多,如下表
這里還有一個(gè)東西沒(méi)有提柑潦,mdpi享言、hdpi、xhdpi渗鬼、xxhdpi览露、xxxhdpi等,它們是用來(lái)修飾Android中的mipmap(Eclipse中是drawable)文件夾及values文件夾譬胎,用來(lái)區(qū)分不同像素密度下的圖片和dimen值差牛,它們與dpi的關(guān)系如下
屏幕分辨率限定符
什么叫屏幕分辨率限定符呢?
我們知道市面上不同分辨率的手機(jī)太多太多了堰乔,而這種方案就是盡可能多的將其列舉出來(lái)偏化,在res目錄下面創(chuàng)建與各種分辨率一一對(duì)應(yīng)的values-xxx 文件夾;然后選定一種基準(zhǔn)分辨率镐侯,然后其它分辨率以該分辨率做參考侦讨,生成對(duì)應(yīng)的dimens文件,如圖:
假設(shè)設(shè)計(jì)圖上的一個(gè)控件的寬度為 720px苟翻,那么布局中就寫(xiě) android:layout_width="@dimen/x720" 搭伤,當(dāng)運(yùn)行程序的時(shí)候,系統(tǒng)會(huì)根據(jù)設(shè)備的分辨率去尋找對(duì)應(yīng)的 dimens.xml 文件袜瞬。例如運(yùn)行在分辨率為 1280x720 的設(shè)備上怜俐,系統(tǒng)會(huì)自動(dòng)找到對(duì)應(yīng)的 values-1280x720 文件夾下的 lay_x.xml 文件,由上圖可知 x720 對(duì)應(yīng)的值為
720.px邓尤,可鋪滿(mǎn)該屏幕寬度拍鲤。運(yùn)行在分辨率為 1920x1080 的設(shè)備上,系統(tǒng)會(huì)自動(dòng)找到對(duì)應(yīng)的 values-1920x1080 文件夾下的 lay_x.xml 文件汞扎,由上圖可知 x720 對(duì)應(yīng)的值為 1080.0px季稳,可鋪滿(mǎn)該屏幕寬度。這樣就達(dá)到了屏幕適配的要求
使用步驟
1澈魄、以設(shè)計(jì)圖最小寬度(單位為 dp)作為基準(zhǔn)值景鼠,生成所有設(shè)備對(duì)應(yīng)的 dimens.xml 文件
這些文件當(dāng)然不會(huì)手動(dòng)去寫(xiě),網(wǎng)上已經(jīng)有大神提供了自動(dòng)生成這些文件的插件ScreenMatch。但是這個(gè)插件還是有點(diǎn)問(wèn)題的:
默認(rèn)沒(méi)有適配最小寬度為 320dp 的設(shè)備铛漓。其實(shí)自己測(cè)試還是有很多設(shè)備最小寬度是 320dp 的溯香,所以需要加上。
最小寬度為 392.7272 與 411.4285 的手機(jī)不能達(dá)到完全適配浓恶。原因是該插件的默認(rèn)值都是取整的玫坛,即 392.7272 與 411.4285 在插件中寫(xiě)的是 392 與 411。
基于以上問(wèn)題包晰,我在該插件的源碼上優(yōu)化生成了新的插件ScreenMatch湿镀,由于插件庫(kù)已經(jīng)有原作者的插件了,所以我就不重復(fù)造輪子上傳到插件庫(kù)了伐憾,你直接用本地安裝的方式安裝即可勉痴。
工具使用步驟:
在 Android Studio 中安裝 ScreenMatch 插件
下載插件ScreenMatch到本地,點(diǎn)擊菜單欄上的 File -> Settings -> Plugins -> Install plugin from disk树肃,然后選擇我們剛剛下載的插件蚀腿,最后點(diǎn)擊 “OK”,重啟 Andorid Studio 即可扫外。如下圖所示:
2莉钙、在項(xiàng)目的默認(rèn) values 文件夾中需要一份 dimens.xml 文件
3、執(zhí)行生成
插件安裝好后筛谚,在項(xiàng)目的任意目錄或文件上右鍵磁玉,選擇 ScreenMatch 選項(xiàng)。如下圖:
4驾讲、通過(guò)上面的步驟就已經(jīng)生成了所有設(shè)備對(duì)應(yīng)的 dimens.xml 文件蚊伞。
根據(jù)設(shè)計(jì)圖填寫(xiě)最小寬度基準(zhǔn)值,并填寫(xiě)需要適配的設(shè)備最小寬度 dp 值
步驟 3 是以插件默認(rèn)的最小寬度基準(zhǔn)值為 360dp吮铭,適配的設(shè)備最小寬度為
320,360,384,392.7272,400,410,411.4285,432,480,533,592,600,640,662,720,768,800,811,820,960,961,1024,1280,1365(包含了平板和 TV )生成的文件时迫,但實(shí)際情況要根據(jù)設(shè)計(jì)圖和需求設(shè)置。
例如設(shè)計(jì)圖的最小寬度為 375dp谓晌,則需要更改最小寬度基準(zhǔn)值為 375dp掠拳。如果項(xiàng)目只需要適配手機(jī)的話(huà),適配的設(shè)備最小寬度保留 320,360,384,392.7272,400,410,411.4285,432,480 即可纸肉,若發(fā)現(xiàn)手機(jī)還有其他最小寬度自行加上即可溺欧,也麻煩把該最小寬度提供給我,我們一起來(lái)完善該份適配柏肪。
以上修改需要在配置文件里修改姐刁,即 screenMatch.properties 文件,該配置文件是執(zhí)行完上面第 3 步后自動(dòng)生成在項(xiàng)目的跟目錄下的烦味。如下圖:
AutoLayout適配
用法
(1) 引入autolayout
```
dependencies {
? ? compile project(':autolayout')
}
```
在你的項(xiàng)目的AndroidManifest中注明你的設(shè)計(jì)稿的尺寸聂使。
```
<
meta-data android:name="design_width" android:value="768"></meta-data>
<meta-data android:name="design_height" android:value="1280"></meta-data>
```
(2)Activity中開(kāi)啟設(shè)配
讓你的Activity去繼承AutoLayoutActivity
今日頭條適配方案(AutoSize)
使用步驟
1、添加依賴(lài)
```
api 'me.jessyan:autosize:1.1.2'
```
2、填寫(xiě)項(xiàng)目設(shè)計(jì)圖尺寸
```
<manifest><application><meta-dataandroid:name="design_width_in_dp"android:value="540"/><meta-dataandroid:name="design_height_in_dp"android:value="960"/></application></manifest>
```
有兩種類(lèi)型的布局單位可以選擇柏靶,一個(gè)是 主單位 (dp弃理、sp),一個(gè)是 副單位 (pt宿礁、in案铺、mm)
主單位: 使用 dp蔬芥、sp 為單位進(jìn)行布局梆靖,侵入性最低,會(huì)影響其他三方庫(kù)頁(yè)面笔诵、三方庫(kù)控件以及系統(tǒng)控件的布局效果返吻,但 AndroidAutoSize 也通過(guò)這個(gè)特性,使用 ExternalAdaptManager 實(shí)現(xiàn)了在不修改三方庫(kù)源碼的情況下適配三方庫(kù)的功能
副單位: 使用 pt乎婿、in测僵、mm 為單位進(jìn)行布局,侵入性高谢翎,對(duì)老項(xiàng)目的支持比較好捍靠,不會(huì)影響其他三方庫(kù)頁(yè)面、三方庫(kù)控件以及系統(tǒng)控件的布局效果森逮,可以徹底的屏蔽修改 density 所造成的所有未知和已知問(wèn)題榨婆,但這樣 AndroidAutoSize 也就無(wú)法對(duì)三方庫(kù)進(jìn)行適配
到這里autosize的框架接入就已經(jīng)完成了,已經(jīng)能夠?qū)崿F(xiàn)autosize的基本功能了
進(jìn)階使用
在 AndroidManifest.xml 中填寫(xiě)的設(shè)計(jì)尺寸褒侧,是整個(gè)項(xiàng)目的全局設(shè)計(jì)圖尺寸但是如果某些 Activity 頁(yè)面由于某些原因良风,這個(gè)頁(yè)面的設(shè)計(jì)圖尺寸和在 AndroidManifest.xml 中填寫(xiě)的設(shè)計(jì)圖尺寸不一樣該怎么辦呢?則可以讓這個(gè)頁(yè)面的 Activity 實(shí)現(xiàn) CustomAdapt 闷供,CustomAdapt 接口的第一個(gè)方法可以修改當(dāng)前頁(yè)面的設(shè)計(jì)尺寸烟央。
```
publicclassCustomAdaptActivityextendsAppCompatActivityimplementsCustomAdapt{/**
? ? * 是否按照寬度進(jìn)行等比例適配 (為了保證在高寬比不同的屏幕上也能正常適配, 所以只能在寬度和高度之中選擇一個(gè)作為基準(zhǔn)進(jìn)行適配)
? ? *
? ? * @return {@code true} 為按照寬度進(jìn)行適配, {@code false} 為按照高度進(jìn)行適配
? ? */@OverridepublicbooleanisBaseOnWidth(){returnfalse;}/**
? ? * 設(shè)計(jì)圖尺寸為 1080px * 1920px, 高換算成 dp 為 960 (1920px / 2 = 960dp)
? ? * <p>
? ? * 返回的設(shè)計(jì)尺寸, 單位 dp
? ? * {@link #getSizeInDp} 須配合 {@link #isBaseOnWidth()} 使用, 規(guī)則如下:
? ? * 如果 {@link #isBaseOnWidth()} 返回 {@code true}, {@link #getSizeInDp} 則應(yīng)該返回設(shè)計(jì)圖的總寬度
? ? * 如果 {@link #isBaseOnWidth()} 返回 {@code false}, {@link #getSizeInDp} 則應(yīng)該返回設(shè)計(jì)圖的總高度
? ? * 如果您不需要自定義設(shè)計(jì)圖上的設(shè)計(jì)尺寸, 想繼續(xù)使用在 AndroidManifest 中填寫(xiě)的設(shè)計(jì)圖尺寸, {@link #getSizeInDp} 則返回 {@code 0}
? ? *
? ? * @return 單位 dp
? ? */@OverridepublicfloatgetSizeInDp(){return667;}}
```
如果你想放棄某個(gè)activity的適配你只需要實(shí)現(xiàn) CancelAdapt 接口即可
```
public class CancelAdaptActivity extends AppCompatActivity implements CancelAdapt{
...
}
```
當(dāng)你想要自定義fragment時(shí)你需要在app初始化時(shí)開(kāi)啟支持
在Applicationl類(lèi)的oncreate方法中添加
```
AutoSizeConfig.getInstance().setCustomFragment(true);
```
萬(wàn)能解決方案
在任何情況下本來(lái)適配正常的布局突然出現(xiàn)適配失效,適配異常等問(wèn)題歪脏,只要重寫(xiě) Activity 的 getResources() 方法即可疑俭,如果是 Dialog、PopupWindow 等控件出現(xiàn)適配失效或適配異常婿失,同樣在每次 show() 之前調(diào)用 AutoSize#autoConvertDensity() 即可怠硼。