前言:之前很火的螢幕適配方案不知道大家都去嘗試過寫進專案中沒,應該有一部分人在隔岸觀火,大概的原因就是目前並沒有遇到能把專案重構的適配問題,另一方面就是有的適配方案還沒有很成熟的應用,都不想拿自己的專案去測試。就拿那些github開源庫上面的適配方案來說,沒有幾個人去上面提issues。就在最近我去試了一下今日頭條的適配方案,然後。。。哎~~接著往下看吧
一 ppi和dpi這兩個單位是什麼?有什麼關係?
ppi(Pixels per inch) 指每英寸上的物理畫素數數目,即 "畫素密度“。一般再購買手機的時候都會在引數中看到該裝置的ppi數值,ppi數值越大螢幕顯像效果越好。不過ppi是物理上的概念,是客觀存在的不會改變的值,跟開發中常見的dpi是完全不同的。
dpi(Dots Per Inch)指每英寸有多少個點,最初是用在印刷行業,用來描述每英寸有多少小黑點。dpi被用於Android開發中用來描述螢幕畫素密度的單位,是手機出廠就寫在系統配置中的一個固定數值,一般是固定不變的,除非你root之後去系統檔案中修改這個值,不過手機root有太多的風險,不推薦去root,開發中可以用DisplayMetrics類去獲取dpi數值。
ppi和dpi是沒有任何關係的。有些文件中ppi 等於 dpi的言論都是瞎扯的,它們之間也沒有什麼換算關係,還有的文章說 dpi的取值取決於ppi處於哪個dpi的範圍,然後取這個範圍最大的值,這一點是沒有任何的依據,至於dpi的賦值我們也無法得知手機廠商是根據什麼去確定的。
ppi的數值我們可以通過以下公式算出,一般的話手機引數裡面都能看到ppi的數值,該公式並不適用計算dpi。
dpi不能用上面的公式求出,dpi可以通過DisplayMetrics類的densityDpi屬性獲取當前手機的dpi數值,該類也可以獲取到跟螢幕密度有關的其它屬性。一般獲取DisplayMetrics類有以下方法:
方式1:
//content:Activity,Content,Application.
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
方式2:
//getSystemService可以通過 Activity,Content,Application等獲取.
DisplayMetrics displayMetrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
複製程式碼
二 為什麼dp滿足不了現在的螢幕適配(設計圖按1080x1920設計)?
為什麼強調設計圖呢, 因為設計圖是UI設計師根據APP的型別以及使用場景精心設計的,同時設計稿直接決定app介面預期的顯示效果,決定了每個控制元件預期的大小,而螢幕適配也是要解決在Android尺寸限制的範圍內,按照一套設計圖寫出的佈局要在大部分機型上面顯示效果都跟設計圖一樣。一般的設計師會給一套尺寸,比如1080 X 1920 即 360dp X 640dp 比例 9:16的,或者IOS和Android使用一套設計圖(一般都會讓Android用IOS的設計稿)。在沒有嚴格要求的話我們只是使用了dp來寫佈局, 反正現在一直都是?,漸漸的發現dp已經逃不過設計師的法眼了(好多機型顯示的效果都跟設計圖有較多的差異)。
接著看,國內Android手機的ppi數值是廠商定製的,跟手機的硬體相關,ppi數值越大顯像效果越好,但ppi只是描述了手機硬體方面的畫素密度,並不用於開發中。每種通用的尺寸和密度都涵蓋一個實際螢幕尺寸和密度範圍。例如, 兩部正常螢幕尺寸的裝置在手動測量時,實際螢幕尺寸和 高寬比可能略有不同。類似地,對於兩臺hdpi 螢幕密度的裝置,其實際畫素密度可能略有不同。 Android 將這些差異抽象概括到應用,使您可以提供為通用尺寸和密度設計的 UI,讓系統按需要處理任何最終調整。(有可能Android手機系統出廠設定的dpi數值也會參考該取值範圍的)。
ldpi(低)~120dpi
mdpi(中)~160dpi
hdpi(高)~240dpi
xhdpi(超高)~320dpi
xxhdpi(超超高)~480dpi
xxxhdpi(超超超高)~640dpi
小螢幕至少為 426dp x 320dp
正常螢幕至少為 470dp x 320dp
大螢幕至少為 640dp x 480dp
超大螢幕至少為 960dp x 720dp
複製程式碼
為了簡化螢幕適配,一般機型的dpi的取值會參考上面的範圍,但是總會有一些特殊的機型就是不採納官方的建議。如小米 MIX 2 解析度 2160x1080 螢幕尺寸 為6, ppi為403 獲取到的dpi為440,該解析度下的手機dpi 大致為480。為什麼要強調dpi的數值呢?我想大家都知道我們再佈局的尺寸方面都會選擇dp,因為dp是會隨著解析度的不同而變化的,一般的關係如下:
dpi 120 : 1dp = 0.75px;
dpi 160 : 1dp = 1px;
dpi 240 : 1dp = 1.5px;
dpi 320 : 1dp = 2px;
dpi 480 : 1dp = 3px;
dpi 640 : 1dp = 4px;
計算公式:
px = density * dp;
dp = px / density;
density = dpi / 160;
複製程式碼
根據上面的公式可以看到dpi影響了dp轉px的數值,所以可以說dp適配也就是dpi的適配,對於360dp X 640dp的設計稿來說,對應的解析度為1080 X 1920和1440 X 2560,使用的數值為 1dp = 3px。正常的機型我們使用dp的話基本可以完成適配,但是當碰到解析度一樣dpi不同的手機,比如dpi = 440 1dp = 2.75px 或者 dpi = 420 1dp = 2.625px 的機型的時候,那就懵逼了,如一個Button的寬度為100dp,再dpi = 480的機型中顯示的寬度效果為300px,再dpi = 440顯示的效果寬度為275px,這樣我們的佈局就會跟預期的不太一樣,這是dp無法適配的。
另外現在主流的是大屏手機,長度方向的畫素點一般大於1920px,大致在2040px~2880px之間,但是寬度基本保持再1080px,配置好的手機是1440px,市場90%以上主流手機寬度都是1080px的。如:
華為:
nova 2s ,Mate 10 Pro 等等解析度是2160X1080 dpi = 480 ;
nova 3 2340 x1080 dpi = 480;
小米:
MIX 2040x1080 dpi = 480, MIX2 2160x1080 dpi = 440 ,
Max 2160X1080 dpi = 480等等;
oppo:
R11s 2160x1080 dpi = 480, R15 2280x1080 dpi = 480,
等等...
複製程式碼
手機dpi的大小決定了當前dp轉px的倍數關係,目前大部分機型的dpi都是480,也就是說設計圖上一個元件的margintop 為100dp = 300px,那麼當執行在解析度為1080X2280的機型中該元件相對於設計圖的位置就會偏上,在解析度為1080x1920的機型中正常,這就會導致一個問題,在大屏手機中正好顯示完整的佈局會再小螢幕中就會出現控制元件被遮擋或者控制元件的高度比不一致,最明顯的就是開屏頁的logo位置。這也是dp無法解決的適配問題。
個人而言,適配寬度用dp基本能夠適配,畢竟那些特殊dpi的機型還是少數,寫佈局注意點的話就不會出現太明顯的適配問題。適配高度就需要使用其他的更有效的適配方式了。
三 寬高限定符,AndroidAutoLayout,smallestWidth,今日頭條適配方案怎麼取捨?
寬高限定符適配和smallestWidth適配方案大致思想都是一樣,smallestWidth比寬高限定符更加的智慧可靠。但是這兩種方案需要增加好多資原始檔,想要適配什麼螢幕就要去配置該型別的資原始檔,全域性適配。這兩種適配方案再寬高適配上還是很有效果的。鴻神的AndroidAutoLayout已經停止維護了,我想大家都不會優先考慮這個方案了,這裡也不去討論。今日頭條適配方案我想大家都或多或少的瞭解過,該方案還是比較精簡靈活的,可以自己選擇以寬度適配還是高度適配,下面是在高度緯度上面的測試資料:
設計圖:
360dp X 640dp 解析度為 1080 X 1920 這裡的螢幕高度包括狀態列。
控制元件高度為103dp 高度/螢幕高度 = 0.1609375.
模擬器 1:
解析度為 1080 x 2280 .實際是 1080 X 2136 .狀態列高度Wie:72px.
控制元件高度為103dp 高度/螢幕高度 = 0.1497093. 適配後:0.1609649.
模擬器 2:
解析度為 1080 x 1920 .實際是 1080 X 1776 .狀態列高度Wie:72px.
控制元件高度為103dp 高度/螢幕高度 = 0.18133803.適配後:0.16035.
模擬器3:
解析度為 480 * 800.狀態列高度Wie:36px. 尺寸小於設計圖的.
控制元件高度為103dp 高度/螢幕高度 = 0.19375. 適配後:0.16125.
小米4:
解析度為 1080 x 1920 .狀態列高度Wie:60px.
控制元件高度為103dp 高度/螢幕高度 = 0.16612904. 適配後:0.1609375.
小米MIX2:
解析度為 1080 x 2160 .狀態列高度Wie:66px. 底部虛擬導航鍵高度為:130px
控制元件高度為103dp 高度/螢幕高度 = 0.13940887. 適配後:0.16108374.
OPPO R15:
解析度 1080 x 2280. 尺寸是 6.28 . 狀態列高度為:84px.
控制元件高度為103dp 高度/螢幕高度 = 0.13552631. 適配後:0.1609649.
華為p20:
解析度為 1080 x 2240 .狀態列高度Wie:85px.
控制元件高度為103dp 高度/螢幕高度 = 0.13770053.適配後:0.16087344.
oppo R9s:
解析度為 1080 x 1920 .狀態列高度Wie:54px.
控制元件高度為103dp 高度/螢幕高度 = 0.1609375. 適配後:0.1609375.
複製程式碼
用今日頭條的適配方案後再大屏手機中的高度比基本等於設計圖中的高度比,這樣在螢幕高度相差很大的真機環境中顯示效果會好很多。今日頭條適配方案更加的靈活,我們再適配的時候雖然是全域性的修改,但是我們可以指定特定的介面上不適配(也就是把設定恢復為預設的設定),這樣即使是第三方的介面只要有程式碼就可以選擇適配適配。另外還可以的自由的配置是以寬度為基準還是以高度為基準點去適配,但是兩者不能兼得。
四 今日頭條適配方案到底可行嗎?
那麼問題來了,再日常開發中只是適配寬度的話,遇見的需求不多,適配高度確實是遇見不少,然後我再適配高度的時候發現了問題。當我們用今日頭條適配方案在高度上去適配大屏手機的話(比如解析度為1080X2160)那樣計算出來的dpi的數值肯定會比原數值高好多。比如小米 MIX2 解析度為 1080 X 2160 高度適配之後再高度緯度的dpi數值為523 那麼就是100dp = 317px,正常情況的dpi為440 100dp = 275px。高度適配之後對寬度方向影響很大的。對下表的資料分析能看出,目前流行機型的寬度定大部分都在1080,高度大於1920的機型居多,再大屏手機裡面我們要首選適配高度的問題,先來看下一個簡單的適配問題。
**需求:**開屏頁logo展示位置。
設計稿: 1080px X 1920px 360dp X 640dp。
logo: 大小100dp X 100dp 水平居中,marginTop100dp。topMargin / 螢幕高度:0.15635。
**測試機型:**小米四(1080X1920) vivo x21(1080X2280)。
真機資料未適配前:
未適配前:
小米4:
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: getWidth300
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: getHeight300
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: topMargin / 螢幕高度0.15625
VIVO X21:
10-12 10:31:15.246 23724-23724/cn.screen.adaptation E/WANG: getWidth300
10-12 10:31:15.246 23724-23724/cn.screen.adaptation E/WANG: getHeight300
10-12 10:31:15.246 23724-23724/cn.screen.adaptation E/WANG: topMargin / 螢幕高度0.13157895
我們可以看到小米4手機的topMargin / 螢幕高度跟設計圖的一致。VIVO X21就相差很大了。這樣顯示出來的logo的位置就會跟設計圖設計的有很大的差距,這種差距是隨著手機豎直解析度的增大而增大。
複製程式碼
真機適配後:
高度適配後:
小米4:
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: getWidth300
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: getHeight300
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: topMargin / 螢幕高度0.15625
VIVO X21:
10-12 10:30:33.760 23502-23502/? E/WANG: getWidth356
10-12 10:30:33.760 23502-23502/? E/WANG: getHeight356
10-12 10:30:33.760 23502-23502/? E/WANG: topMargin / 螢幕高度0.15614036
我們可以明顯的看到logo的topMargin / 螢幕高度基本跟設計搞的一致,這樣就達到了logo在大多數機型上面顯示的效果跟設計稿的一樣。但是可以發現logo的寬高都增加了56px,這也是因為適配高度的時候更改了dpi的數值,dpi的數值偏大就會造成全域性的dp轉px的倍率變大,這樣我們的logo的大小和該介面的其它的控制元件的大小都會有影響。
複製程式碼
總結:
螢幕適配任重而道遠,我們要針對設計稿,針對介面,針對控制元件去選擇我們的適配方式,技術好並不代表好用,有的時候會反其道而行之。本人還是很喜歡今日頭條適配方案的,用註解做起來逼格瞬間提升,想再那個介面適配就在那個介面適配,想取消適配就取消適配,也就一個註解的事。另外還有一點就是,適配方案推出那麼多時間也不短了,有幾個開發者實戰了呢?所謂實踐出真理今日頭條適配方案坑很多,我們一起慢慢踩~~歡迎大家提出文章裡面的錯誤,大家共同學習!
註解版今日頭條適配方案 (供參考學習)
五 主流機型
注: 以下機型的dpi數值只有一部分得到真機驗證,其餘存在些許誤差望更正,體現在(寬/density)這個數值上。
華為-榮耀系列:
機型 | 解析度 | ppi | 尺寸 | 寬/density | 高/density |
---|---|---|---|---|---|
華為暢享8 | 1440x720 | 269 | 5.99 | 360dp | 720dp |
華為nova 2 | 1920x1080 | 440 | 5 | 360dp | 640dp |
華為P9 | 1920x1080 | 424 | 5.2 | 360dp | 640dp |
華為Mate 9 | 1920x1080 | 373 | 5.9 | 360dp | 640dp |
華為P10 | 1920x1080 | 432 | 5.1 | 360dp | 640dp |
華為Mate 10 Pro | 2160x1080 | 402 | 6 | 360dp | 720dp |
華為nova 2s | 2160x1080 | 402 | 6 | 360dp | 720dp |
華為暢享8 Plus | 2160x1080 | 407 | 5.93 | 360dp | 720dp |
華為Mate 10 Pro | 2160x1080 | 402 | 6 | 360dp | 720dp |
華為nova 2s | 2160x1080 | 402 | 6 | 360dp | 720dp |
華為P20 Pro | 2240x1080 | 408 | 6.1 | 360dp | 746.7dp |
華為P20 | 2244x1080 | 428 | 5.8 | 360dp | 748dp |
華為nova 3e | 2280x1080 | 432 | 5.84 | 360dp | 760dp |
華為nova 3i | 2340x1080 | 409 | 6.3 | 360dp | 780dp |
華為nova 3 | 2340x1080 | 409 | 6.3 | 360dp | 780dp |
華為Mate 10 | 2560x1440 | 498 | 5.9 | 360dp | 640dp |
華為Mate 20 | 2560x1440 | 482 | 6.1 | 360dp | 640dp |
華為Mate RS保時捷版 | 2880x1440 | 537 | 6 | 360dp | 720dp |
小米:
機型 | 解析度 | ppi | 尺寸 | 寬/density | 高/density |
---|---|---|---|---|---|
小米紅米6 | 1440x720 | 295 | 5.45 | 360dp | 720dp |
小米Max 2 | 1920x1080 | 342 | 6.44 | 360dp | 640dp |
小米5X | 1920x1080 | 401 | 5.5 | 360dp | 640dp |
小米6 | 1920x1080 | 428 | 5.15 | 360dp | 640dp |
小米Max 2 | 1920x1080 | 342 | 6.44 | 360dp | 640dp |
小米MIX | 2040x1080 | 361 | 6.4 | 360dp | 680dp |
小米6X | 2160x1080 | 403 | 6.0 | 360dp | 720dp |
小米MIX 2s | 2160x1080 | 403 | 6.0 | 360dp | 720dp |
小米紅米Note 5 | 2160x1080 | 403 | 6.0 | 360dp | 720dp |
小米Max 3 | 2160x1080 | 350 | 6.9 | 360dp | 720dp |
小米MIX 2 | 2160x1080 | 403 | 6.0 | 392.7dp | 785.5dp |
小米8 SE | 2244x1080 | 424 | 5.88 | 360dp | 748dp |
小米8 | 2248x1080 | 402 | 6.21 | 360dp | 749.3dp |
小米8透明探索版 | 2248x1080 | 402 | 6.21 | 360dp | 749.3dp |
小米紅米6 Pro | 2280x1080 | 432 | 5.84 | 360dp | 760dp |
OPPO
機型 | 解析度 | ppi | 尺寸 | 寬/density | 高/density |
---|---|---|---|---|---|
OPPO A57 | 1280x720 | 282 | 5.2 | 360dp | 640dp |
OPPO A83 | 1440x720 | 282 | 5.7 | 360dp | 720dp |
OPPO A5 | 1520x720 | 271 | 6.2 | 360dp | 760dp |
OPPO R9S | 1920X1080 | 401 | 5.5 | 360dp | 640dp |
OPPO R11 | 1920x1080 | 401 | 5.5 | 360dp | 640dp |
OPPO R11 Plus | 1920x1080 | 367 | 6 | 360dp | 640dp |
OPPO R11s | 2160x1080 | 401 | 6.0 | 360dp | 720dp |
OPPO R11s Plus | 2160x1080 | 376 | 6.43 | 360dp | 720dp |
OPPO R15 | 2280x1080 | 402 | 6.28 | 360dp | 760dp |
OPPO A3 | 2280x1080 | 405 | 6.2 | 360dp | 760dp |
OPPO R17 | 2340x1080 | 402 | 6.4 | 360dp | 780dp |
OPPO Find X | 2340x1080 | 401 | 6.42 | 360dp | 780dp |
OPPO R17 Pro | 2340x1080 | 402 | 6.4 | 360dp | 780dp |
VIVO
機型 | 解析度 | ppi | 尺寸 | 寬/density | 高/density |
---|---|---|---|---|---|
vivo Y71 | 1440x720 | 269 | 6.0 | 360dp | 720dp |
vivo Y83 | 1520x720 | 270 | 6.22 | 360dp | 760dp |
vivo x7Plus | 1920x1080 | 386 | 5.7 | 360dp | 640dp |
vivo X20Plus | 2160x1080 | 376 | 6.43 | 360dp | 720dp |
vivo X20 | 2160x1080 | 401 | 6.0 | 360dp | 720dp |
vivo Y97 | 2280x1080 | 401 | 6.3 | 360dp | 760dp |
vivo X21螢幕指紋版 | 2280x1080 | 402 | 6.28 | 360dp | 760dp |
vivo X21 | 2280x1080 | 402 | 6.28 | 360dp | 760dp |
vivo Z1 | 2280x1080 | 403 | 6.26 | 360dp | 760dp |
vivo Y85 | 2280x1080 | 403 | 6.26 | 360dp | 760dp |
vivo NEX | 2316x1080 | 388 | 6.59 | 360dp | 772dp |
vivo X23 | 2340x1080 | 402 | 6.4 | 360dp | 780dp |
魅族
機型 | 解析度 | ppi | 尺寸 | 寬/density | 高/density |
---|---|---|---|---|---|
魅族魅藍S6 | 1440x720 | 282 | 5.7 | 360dp | 720dp |
魅族15 | 1920x1080 | 403 | 5.46 | 360dp | 640dp |
魅族PRO 7 | 1920x1080 | 424 | 5.2 | 360dp | 640dp |
魅族魅藍Note 6 | 1920x1080 | 401 | 5.5 | 360dp | 640dp |
魅族16th | 2160x1080 | 402 | 6 | 360dp | 720dp |
魅族16th Plus | 2160x1080 | 372 | 6.5 | 360dp | 720dp |
魅族16 X | 2160x1080 | 402 | 6.0 | 360dp | 720dp |
魅族15 Plus | 2560x1440 | 494 | 5.95 | 360dp | 640dp |
魅族PRO 7 Plus | 2560x1440 | 515 | 5.7 | 360dp | 640dp |
錘子
機型 | 解析度 | ppi | 尺寸 | 寬/density | 高/density |
---|---|---|---|---|---|
錘子科技Smartisan T2 | 1920x1080 | 445 | 4.95 | 360dp | 640dp |
錘子科技堅果Pro | 1920x1080 | 401 | 5.5 | 360dp | 640dp |
錘子科技堅果Pro 2S | 2160x1080 | 402 | 6.0 | 360dp | 720dp |
錘子科技堅果Pro 2 | 2160x1080 | 402 | 6.0 | 360dp | 720dp |
錘子科技堅果R1 | 2240x1080 | 403 | 6.17 | 360dp | 746.7dp |