更新:由于該適配方案越來(lái)越多人使用,也有很多人遇到不太理解的問(wèn)題抵皱。所以為了大家更好的使用帖汞,我將文章很多內(nèi)容更新了,老用戶(hù)可以重新看下整篇文章砾肺。主要更新的內(nèi)容是使用方法跟以前不同了挽霉,以前是不改變最小寬度基準(zhǔn)值,根據(jù) UI 圖來(lái)計(jì)算布局中設(shè)置的值”渫簦現(xiàn)在改成最小寬度基準(zhǔn)值與設(shè)計(jì)圖一致侠坎,然后設(shè)計(jì)圖標(biāo)注多少 dp 就寫(xiě)多少 dp,非常方便裙盾。還整理了評(píng)論區(qū)比較多人問(wèn)到的問(wèn)題作統(tǒng)一回答实胸。
前言
網(wǎng)上關(guān)于屏幕適配的文章已經(jīng)鋪天蓋地了,為什么我還要講番官?因?yàn)榫W(wǎng)上現(xiàn)在基本都是使用屏幕分辨率限定符進(jìn)行適配庐完,即每種屏幕分辨率的設(shè)備需要定義一套 dimens.xml 文件。由于不同分辨率的設(shè)備太多了徘熔,而且有些設(shè)備還有虛擬按鍵(例如華為手機(jī))门躯,這樣就還需要每個(gè)有虛擬按鍵的設(shè)備加多一套 dimens.xml 文件,再加上平板那些你會(huì)發(fā)現(xiàn) dimens.xml 文件所占的體積已經(jīng)超過(guò)2M了酷师!這絕對(duì)不是我們想要的讶凉。
我這里要講的是使用sw<N>dp限定符染乌,即 smallestWidth(最小寬度) 限定符來(lái)進(jìn)行適配,使用這種方式只需要少量 dimens.xml 文件即可達(dá)到適配懂讯,而且根本不用考慮虛擬按鍵的問(wèn)題慕匠。如果只適配手機(jī),dimens.xml 文件所占的體積只有 100 多 KB域醇,即使加上平板和 TV台谊,也就500多KB,完全可以接收譬挚。這種方案已經(jīng)在自己多個(gè)項(xiàng)目中應(yīng)用過(guò)了锅铅,經(jīng)過(guò)幾十臺(tái)手機(jī)測(cè)試過(guò),基本不會(huì)出現(xiàn)適配有問(wèn)題的情況减宣。制作生成對(duì)應(yīng) dimens.xml 文件插件(后面會(huì)講)的作者也說(shuō)過(guò)他在待過(guò)的兩家大公司實(shí)踐過(guò)盐须,所以請(qǐng)放心使用。
為什么要進(jìn)行屏幕適配漆腌?
關(guān)于為什么要進(jìn)行屏幕適配贼邓,什么是 dp、dpi 這些概念我就不去一一講解了闷尿,網(wǎng)上很多文章塑径。這里我推薦幾篇講的比較好的:
Android屏幕適配全攻略(最權(quán)威的官方適配指導(dǎo))
屏幕分辨率限定符與 smallestWidth 限定符適配原理
屏幕分辨率限定符適配原理
屏幕分辨率限定符適配需要在 res 文件夾下創(chuàng)建各種屏幕分辨率對(duì)應(yīng)的 values-xxx 文件夾,如下圖:
然后根據(jù)一個(gè)基準(zhǔn)分辨率填具,例如基準(zhǔn)分辨率為 1280x720统舀,將寬度分成 720 份,取值為 1px~720px劳景,將高度分成 1280 份誉简,取值為 1px~1280px,生成各種分辨率對(duì)應(yīng)的 dimens.xml 文件盟广。如下分別為分辨率 1280x720 與 1920x1080 所對(duì)應(yīng)的橫向dimens.xml 文件:
假設(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á)到了屏幕適配的要求跃惫!
smallestWidth 限定符 適配原理
smallestWidth 限定符適配原理與屏幕分辨率限定符適配原理一樣叮叹,系統(tǒng)都是根據(jù)限定符去尋找對(duì)應(yīng)的 dimens.xml 文件。例如程序運(yùn)行在最小寬度為 360dp 的設(shè)備上爆存,系統(tǒng)會(huì)自動(dòng)找到對(duì)應(yīng)的 values-sw360dp 文件夾下的 dimens.xml 文件蛉顽。區(qū)別就在于屏幕分辨率限定符適配是拿 px 值等比例縮放,而 smallestWidth 限定符適配是拿 dp 值來(lái)等比縮放而已先较。需要注意的是“最小寬度”是不區(qū)分方向的携冤,即無(wú)論是寬度還是高度,哪一邊小就認(rèn)為哪一邊是“最小寬度”闲勺。如下分別為最小寬度為 360dp 與最小寬度為 640dp 所對(duì)應(yīng)的 dimens.xml 文件:
獲取設(shè)備最小寬度代碼為:
DisplayMetrics dm =newDisplayMetrics();? ? ? ? getWindowManager().getDefaultDisplay().getMetrics(dm);intheightPixels = ScreenUtils.getScreenHeight(this);intwidthPixels = ScreenUtils.getScreenWidth(this);floatdensity = dm.density;floatheightDP = heightPixels / density;floatwidthDP = widthPixels / density;floatsmallestWidthDP;if(widthDP < heightDP) {? ? ? ? ? ? smallestWidthDP = widthDP;? ? ? ? }else{? ? ? ? ? ? smallestWidthDP = heightDP;? ? ? ? }
ScreenUtils——>ScreenUtils
為什么選擇 smallestWidth 限定符適配曾棕?
既然原理都一樣,都需要多套 dimens.xml 文件菜循,那為什么要選擇 smallestWidth 限定符適配呢翘地?
屏幕分辨率限定符適配是根據(jù)屏幕分辨率的,Android 設(shè)備分辨率一大堆癌幕,而且還要考慮虛擬鍵盤(pán)衙耕,這樣就需要大量的 dimens.xml 文件。因?yàn)闊o(wú)論手機(jī)屏幕的像素多少勺远,密度多少橙喘,90% 的手機(jī)的最小寬度都為 360dp,所以采用 smallestWidth 限定符適配只需要少量 dimens.xml 文件即可谚中。
屏幕分辨率限定符適配采用的是 px 單位渴杆,而 smallestWidth 限定符適配采用的單位是 dp 和 sp,dp 和 sp 是google 推薦使用的計(jì)量單位宪塔。又由于很多應(yīng)用要求字體大小隨系統(tǒng)改變,所以字體單位使用 sp 也更靈活囊拜。
屏幕分辨率限定符適配需要設(shè)備分辨率與 values-xx 文件夾完全匹配才能達(dá)到適配某筐,而 smallestWidth 限定符適配尋找 dimens.xml 文件的原理是從大往小找,例如設(shè)備的最小寬度為 360dp冠跷,就會(huì)先去找 values-360dp南誊,發(fā)現(xiàn)沒(méi)有則會(huì)向下找 values-320dp,如果還是沒(méi)有才找默認(rèn)的 values 下的 demens.xml 文件蜜托,所以即使沒(méi)有完全匹配也能達(dá)到不錯(cuò)的適配效果抄囚。
使用步驟
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 即可。如下圖所示:
在項(xiàng)目的默認(rèn) values 文件夾中需要一份 dimens.xml 文件
我在github 源碼已經(jīng)提供了一份喉磁,直接復(fù)制過(guò)來(lái)即可谓苟。
執(zhí)行生成
插件安裝好后,在項(xiàng)目的任意目錄或文件上右鍵协怒,選擇 ScreenMatch 選項(xiàng)涝焙。如下圖:
然后選擇在哪個(gè) module 下執(zhí)行適配。即基于哪個(gè) module 下的 res/values/dimens.xml 文件作為基準(zhǔn) dimens.xml 文件孕暇,生成的其他尺寸 dimens.xml 文件放在哪個(gè) module 下仑撞。例如選擇 app,然后點(diǎn)擊 OK 妖滔,出現(xiàn)如下界面表示生成文件成功隧哮。如下圖:
然后再看看 res 目錄下會(huì)自動(dòng)生成一堆 dimens.xml 文件,如下圖:
通過(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)目的跟目錄下的袁滥。如下圖:
打開(kāi)配置文件,修改下圖中 1灾螃、3题翻、4 的值即可。(圖中單位均為 dp)
1:最小寬度基準(zhǔn)值腰鬼,填寫(xiě)設(shè)計(jì)圖的最小寬度值即可嵌赠。
2:插件默認(rèn)適配的最小寬度值,即默認(rèn)情況下會(huì)生成如下值的 dimens.xml 文件熄赡。
3:需要適配的最小寬度值(如果是小數(shù)姜挺,則保留4位小數(shù)。例如 392.727272...彼硫,則取 392.7272)炊豪,即你想生成哪些 dimens.xml 文件。
4:忽略不需要適配的最小寬度值拧篮,即忽略掉插件默認(rèn)生成的 dimens.xml 文件词渤。
配置文件修改完成后,重新執(zhí)行第 3 步串绩,生成新的 dimens.xml 文件缺虐。
當(dāng)然!如果你的設(shè)計(jì)圖也是標(biāo)準(zhǔn)的 360dp礁凡,那么上面的步驟你可以忽略高氮。直接復(fù)制我 github 上你需要的 dimens.xml 文件到你的項(xiàng)目即可,默認(rèn)的 values 文件夾下也需要一份顷牌。
2剪芍、根據(jù)設(shè)計(jì)圖標(biāo)注,在布局寫(xiě)上對(duì)應(yīng)的值窟蓝。
設(shè)計(jì)圖標(biāo)注多少 dp紊浩,布局中就寫(xiě)多少 dp ,非常方便疗锐!
大多數(shù) UI 設(shè)計(jì)師提供設(shè)計(jì)圖有如下幾種方式:
上傳到藍(lán)湖:顯示多少 dp 就寫(xiě)多少 dp。
psd 源文件:用像素大廚查看费彼,顯示多少 dp 就寫(xiě)多少 dp(注意像素大廚需要選擇與設(shè)計(jì)圖對(duì)應(yīng)的dpi 進(jìn)行顯示)
dp 單位的設(shè)計(jì)圖:標(biāo)注多少 dp 就寫(xiě)多少 dp滑臊。
px 單位的設(shè)計(jì)圖:叫 UI 設(shè)計(jì)師標(biāo)注為 dp 單位或跟她要 psd 源文件,如果都不行箍铲,那自己算吧雇卷!
舉例:例如設(shè)計(jì)圖上一個(gè)Button 的寬為 360dp,高為 50dp,字體大小為 15 sp关划,在布局中則這樣使用:
代碼中動(dòng)態(tài)設(shè)置 dp 或 sp:
如果需要在代碼中動(dòng)態(tài)設(shè)置 dp 或 sp小染,則需要通過(guò) getDimension()方法獲取對(duì)應(yīng)資源文件下的 dp 或 sp 值再設(shè)置(具體參考 github 上的 demo)。如下:
/*獲取sp值*/floatpxValue = getResources().getDimension(R.dimen.sp_15);//獲取對(duì)應(yīng)資源文件下的sp值intspValue = ConvertUtils.px2sp(this, pxValue);//將px值轉(zhuǎn)換成sp值mTvShowParams.setTextSize(spValue);//設(shè)置文字大小/*獲取dp值*/floatpxValue2 = getResources().getDimension(R.dimen.dp_360);//獲取對(duì)應(yīng)資源文件下的dp值intdpValue = ConvertUtils.px2dp(this, pxValue2);//將px值轉(zhuǎn)換成dp值
使用步驟總結(jié)
說(shuō)了這么多贮折,其實(shí)只需要簡(jiǎn)單的 2 步:
以設(shè)計(jì)圖最小寬度(單位為 dp)作為基準(zhǔn)值裤翩,利用插件生成所有設(shè)備對(duì)應(yīng)的 dimens.xml 文件
根據(jù)設(shè)計(jì)圖標(biāo)注,標(biāo)注多少 dp调榄,布局中就寫(xiě)多少dp踊赠,格式為@dimen/dp_XX。
怎么適配其他 module?
問(wèn)題:在項(xiàng)目的其他 module 中怎么實(shí)現(xiàn)適配每庆?難道也要多套 dimens 文件筐带?
解決:并不需要多套 dimens 文件,只需要在 values 文件夾下有一套與 app module 一樣的 dimens 文件即可達(dá)到適配缤灵。因?yàn)榻?jīng)過(guò)編譯伦籍,所有 module 中的 dimen 數(shù)據(jù)都會(huì)統(tǒng)一歸類(lèi)到主 module(即 app module)中的 values/dimens.xml 文件中了,然后系統(tǒng)又會(huì)根據(jù)你設(shè)置的值去找對(duì)應(yīng) values-swxxxdp 文件夾下的dimens.xml 文件中的值腮出。
驗(yàn)證:將我 github 上的 demo 分別運(yùn)行在不同 widthDP 的設(shè)備上(用模擬器即可)帖鸦,然后觀(guān)察顯示的效果會(huì)發(fā)現(xiàn)確實(shí)是這樣的。
常見(jiàn)問(wèn)題匯總
為什么寬度適配了利诺,高度有時(shí)候沒(méi)有完全適配富蓄?
因?yàn)楦鞣N屏幕高寬比并不是固定的,有16:9慢逾、4:3立倍,還有全面屏的19.5:9等等,如果強(qiáng)行將寬高都適配那只會(huì)導(dǎo)致布局變形侣滩。
例如一個(gè)控件的寬高為360dp和640dp口注,如果將它顯示在寬高為360dp和640dp的設(shè)備上是正常鋪滿(mǎn)整個(gè)屏幕的,但是顯示在寬高為360dp和780dp的設(shè)備上高度則不能鋪滿(mǎn)君珠,如果你讓高度鋪滿(mǎn)寝志,而寬度又保持不變,那就會(huì)出現(xiàn)變形的情況策添。所以這也就是為什么目前市面上的屏幕適配方案只能以寬或高一個(gè)維度去適配材部,另一個(gè)方向用滑動(dòng)或權(quán)重的方式去適配的原因。
那你為什么說(shuō)高度也能適配呢唯竹?
這里說(shuō)的高度也能適配指的是在不同分辨率和密度的手機(jī)上能達(dá)到等比縮放的適配乐导,其他屏幕適配方案也是一樣的。
如何同時(shí)適配橫豎屏浸颓?
方案一:(不推薦)
計(jì)算出設(shè)備寬度和高度的dp值物臂,然后生成對(duì)應(yīng)的寬高 dimens.xml 文件旺拉。然后去掉所有 values-swXXXdp 目錄上的s,即改為 values-wXXXdp棵磷。這樣設(shè)備不管橫豎屏都能找到對(duì)應(yīng)的 values-wXXXdp 目錄下的? dimens.xml 文件了蛾狗。 雖然也能達(dá)到一定程度的適配,但是這樣會(huì)增加很多 dimens.xml 文件仪媒,而且使用豎屏的設(shè)計(jì)圖顯示出來(lái)的效果也不夠好沉桌。
方案二:(推薦)
因?yàn)闄M屏?xí)r寬高變化太大,想要橫屏?xí)r也能完全適配规丽,那就只能讓設(shè)計(jì)師出一套橫屏的設(shè)計(jì)圖蒲牧,然后單獨(dú)寫(xiě)一套橫屏的布局文件。
注意:smallestWidth 限定符適配的效果是讓不同分辨率和密度的設(shè)備上能達(dá)到以設(shè)計(jì)圖等比縮放的適配赌莺,如果設(shè)備與設(shè)計(jì)圖相差太大時(shí)并不能達(dá)到很好的適配效果冰抢,需要單獨(dú)出圖,其他屏幕適配方案也是一樣的艘狭。
如何適配平板挎扰、TV?
同橫屏道理一樣巢音,平板遵倦、TV 與手機(jī)的寬高差距太大,想要平板官撼、TV 也能完全適配梧躺,那就只能讓設(shè)計(jì)師出一套平板、TV 的設(shè)計(jì)圖傲绣,然后單獨(dú)寫(xiě)一套平板掠哥、TV 的布局文件。
注意:再說(shuō)一遍秃诵,smallestWidth 限定符適配的效果是讓不同分辨率和密度的設(shè)備上能達(dá)到以設(shè)計(jì)圖等比縮放的適配续搀,如果設(shè)備與設(shè)計(jì)圖相差太大時(shí)并不能達(dá)到很好的適配效果,需要單獨(dú)出圖菠净,其他屏幕適配方案也是一樣的禁舷。
作者:wn