前段時(shí)間今日頭條開源了屏幕適配方案坚弱,前段時(shí)間大體的看了一下稍味,正好這兩天有時(shí)間隧熙,仔細(xì)研究一下和總結(jié)一下適配方案婉弹。
在了解適配方案之前睬魂,先來一遍dp,dpi,density概念吧!
px : 是pixel的縮寫镀赌,pixel即像素,平時(shí)所說的設(shè)備的分辨率是多少氯哮,這里的單位就是px。
dp: 指的是設(shè)備獨(dú)立像素商佛,以dp為尺寸單位的控件喉钢,在不同分辨率和尺寸的手機(jī)上代表了不同的真實(shí)像素,比如在分辨率較低的手機(jī)中良姆,可能1dp=1px,而在分辨率較高的手機(jī)中出牧,可能1dp=2px⌒危或者是1dp=3px;
那么這個(gè)dp是如何計(jì)算的呢?
我們都知道一個(gè)公式: px = dp(dpi/160) 系統(tǒng)都是通過這個(gè)來判斷px和dp的數(shù)學(xué)關(guān)系评抚,那么這里又出現(xiàn)了一個(gè)問題豹缀,dpi是什么呢?
dpi:是像素密度慨代,指的是在系統(tǒng)軟件上指定的單位尺寸的像素?cái)?shù)量邢笙,它往往是寫在系統(tǒng)出廠配置文件的一個(gè)固定值。
屏幕尺寸侍匙、像素密度氮惯、分辨率的三者關(guān)系:
1、dp適配解決方案:
android中在渲染屏幕時(shí)想暗,都會(huì)將我們在xml中的dp單位轉(zhuǎn)化為px,去渲染到設(shè)備中妇汗,用到的轉(zhuǎn)換單位如下:
px =dp * density;
density=dpi/160;
px=dp*(dpi/160);
而dpi是根據(jù)設(shè)備的屏幕真實(shí)分辨率和尺寸大小進(jìn)行計(jì)算得到的,每個(gè)設(shè)備可能不一樣说莫,這也是Android設(shè)備進(jìn)行碎片化的原因杨箭,和總是有人進(jìn)行探索適配方案的原因。
假如設(shè)備的分辨率為1920*1080,屏幕大小為5英寸储狭,則通過上面的換算公式則得到該設(shè)備的dpi為 :對角線的像素個(gè)數(shù) 2203/屏幕大小5=440dpi,由此得到density=440/160=2.75 互婿,由此可以換算出屏幕寬度的dp=1080/2.75=392dp
但是捣郊,假如我們的UI給的設(shè)計(jì)圖為360dp的,這種情況慈参,顯然屏幕尺寸要比設(shè)計(jì)圖寬呛牲,這種情況下,即使使用dp適配驮配,也很難達(dá)到不同設(shè)備之間顯示相同的效果娘扩,還有可能出現(xiàn)部分設(shè)備展示不全的情況。
而且上述屏幕尺寸僧凤、分辨率和像素密度的關(guān)系畜侦,很多設(shè)備并沒有按此規(guī)則來實(shí)現(xiàn), 因此dpi的值非常亂躯保,沒有規(guī)律可循旋膳,從而導(dǎo)致使用dp適配效果差強(qiáng)人意。
一般給我們的設(shè)計(jì)圖原則為:
7201280的分辨率途事,density為2验懊,
10801920的分辨率,density為3尸变,(常見情況)
1440*2560的分辨率义图,density為4,
所以召烂,一般會(huì)以360dp去適配應(yīng)用程序(即640dp*36dp)碱工,故如上的設(shè)備顯然要比設(shè)計(jì)圖寬,那怎么辦呢奏夫?再回頭看這個(gè)公式
px=dp*density
屏幕的像素值每個(gè)設(shè)備已經(jīng)是固定的怕篷,即px固定,當(dāng)設(shè)計(jì)圖出來的時(shí)候酗昼,一般dp也是固定的廊谓,即如上介紹的360dp,為了在不同的設(shè)備上顯示相同的效果,所以只能修改density麻削。
每個(gè)設(shè)備的分辨率和尺寸大小的不同蒸痹,則density會(huì)有好多種,而要實(shí)現(xiàn)不同的設(shè)備之間顯示相同的效果呛哟,只要修改density為我們希望的density叠荠,覆蓋掉系統(tǒng)本身的density即可, density=screenWidth/360,這里360看實(shí)際情況竖共,看UI給的設(shè)計(jì)圖的到底是多大的蝙叛。如果我們想在所有設(shè)備上顯示完全一致,其實(shí)是不現(xiàn)實(shí)的公给,因?yàn)槠聊桓邔挶炔皇枪潭ǖ模?6:9借帘、4:3甚至其他寬高比層出不窮蜘渣,寬高比不同,顯示完全一致就不可能了肺然。
只要在一個(gè)維度上進(jìn)行適配蔫缸,也就是說假如頁面是上下滑動(dòng)的,我們只要確定好寬度這一個(gè)維度適配好即可际起,同理拾碌,如果頁面是左右滑動(dòng)的,只要設(shè)置好高度這個(gè)維度就可以街望。
如上是今日頭條團(tuán)隊(duì)的解決方案,即動(dòng)態(tài)的修改設(shè)備的density值校翔,達(dá)到不同分辨率設(shè)備的適配。
blankj大佬的封裝的解決方案:附上地址:https://github.com/Blankj/AndroidUtilCode
核心代碼如下:
private static void adaptScreen(final Activity activity,
final int sizeInPx,
final boolean isVerticalSlide) {
final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics();
final DisplayMetrics appDm = App.getAppContext().getResources().getDisplayMetrics();
final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics();
if (isVerticalSlide) {
activityDm.density = activityDm.widthPixels / (float) sizeInPx;
} else {
activityDm.density = activityDm.heightPixels / (float) sizeInPx;
}
activityDm.scaledDensity = activityDm.density * (systemDm.scaledDensity / systemDm.density);
activityDm.densityDpi = (int) (160 * activityDm.density);
appDm.density = activityDm.density;
appDm.scaledDensity = activityDm.scaledDensity;
appDm.densityDpi = activityDm.densityDpi;
}
假如要使用第三方的UI界面的時(shí)候灾前,重新設(shè)置為系統(tǒng)的density即可
public static void cancelAdaptScreen(final Activity activity) {
final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics();
final DisplayMetrics appDm = App.getAppContext().getResources().getDisplayMetrics();
final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics();
activityDm.density = systemDm.density;
activityDm.scaledDensity = systemDm.scaledDensity;
activityDm.densityDpi = systemDm.densityDpi;
appDm.density = systemDm.density;
appDm.scaledDensity = systemDm.scaledDensity;
appDm.densityDpi = systemDm.densityDpi;
}
這里展示一下如上適配方案適配效果:
480*800分辨率手機(jī)防症,density為1.5
768*1280分辨率手機(jī),density為2:
800*1280的平板哎甲,density為1.5
1080*1920分辨率手機(jī) 蔫敲,density為3:
2、寬高限定符適配
簡單說炭玫,就是窮舉市面上所有的Android手機(jī)的寬高像素值即:
這個(gè)時(shí)候奈嘿,如果我們的UI設(shè)計(jì)界面使用的就是基準(zhǔn)分辨率,那么我們就可以按照設(shè)計(jì)稿上的尺寸填寫相對應(yīng)的dimens引用了,而當(dāng)APP運(yùn)行在不同分辨率的手機(jī)中時(shí)吞加,這些系統(tǒng)會(huì)根據(jù)這些dimens引用去該分辨率的文件夾下面尋找對應(yīng)的值裙犹。這樣基本解決了我們的適配問題,而且極大的提升了我們UI開發(fā)的效率衔憨。
但是這個(gè)方案有一個(gè)致命的缺陷伯诬,那就是需要精準(zhǔn)命中才能適配,比如1920x1080的手機(jī)就一定要找到1920x1080的限定符巫财,否則就只能用統(tǒng)一的默認(rèn)的dimens文件了。而使用默認(rèn)的尺寸的話哩陕,UI就很可能變形平项,簡單說,就是容錯(cuò)機(jī)制很差悍及,而且再一些機(jī)型上面闽瓢,即使配置了,也不會(huì)去對應(yīng)的分辨率下去找心赶。
3扣讼、smallestWidth適配
smallestWidth適配,或者叫sw限定符適配缨叫。指的是Android會(huì)識別屏幕可用高度和寬度的最小尺寸的dp值(其實(shí)就是手機(jī)的寬度值)椭符,然后根據(jù)識別到的結(jié)果去資源文件中尋找對應(yīng)限定符的文件夾下的資源文件荔燎。
這種機(jī)制和上文提到的寬高限定符適配原理上是一樣的,都是系統(tǒng)通過特定的規(guī)則來選擇對應(yīng)的文件销钝。
舉個(gè)例子有咨,小米5s的dpi是480,橫向像素是1080px,根據(jù)px=dp(dpi/160)蒸健,橫向的dp值是1080/(480/160),也就是360dp,系統(tǒng)就會(huì)去尋找是否存在value-sw360dp的文件夾以及對應(yīng)的資源文件座享。
smallestWidth限定符適配和寬高限定符適配最大的區(qū)別在于,前者有很好的容錯(cuò)機(jī)制似忧,如果沒有value-sw360dp文件夾渣叛,系統(tǒng)會(huì)向下尋找,比如離360dp最近的只有value-sw350dp盯捌,那么Android就會(huì)選擇value-sw350dp文件夾下面的資源文件淳衙。這個(gè)特性就完美的解決了上文提到的寬高限定符的容錯(cuò)問題。
這里展示一下如上適配方案適配效果:
480*800分辨率手機(jī)挽唉,density為1.5
768*1280分辨率手機(jī)滤祖,density為2:
800*1280分辨率平板,density為1.5:
1080*1920分辨率手機(jī) 瓶籽,density為3:
附上上述Demo地址:https://github.com/OnexZgj/AdapterScreen 待后期完善