Android屏幕適配前先了解這些

前言:之前很火的屏幕適配方案不知道大家都去嘗試過寫進項目中沒,應該有一部分人在隔岸觀火,大概的原因就是目前並沒有遇到能把項目重構的適配問題,另一方面就是有的適配方案還沒有很成熟的應用,都不想拿自己的項目去測試。就拿那些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數值也會參考該取值範圍的)。

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的大小和該界面的其它的控件的大小都會有影響。    

總結:

屏幕適配任重而道遠,我們要針對設計稿,針對界面,針對控件去選擇我們的適配方式,技術好並不代表好用,有的時候會反其道而行之。本人還是很喜歡今日頭條適配方案的,用註解做起來逼格瞬間提升,想再那個界面適配就在那個界面適配,想取消適配就取消適配,也就一個註解的事。另外還有一點就是,適配方案推出那麼多時間也不短了,有幾個開發者實戰了呢?所謂實踐出真理今日頭條適配方案坑很多,我們一起慢慢踩~~歡迎大家提出文章裏面的錯誤,大家共同學習!

參考 https://developer.android.google.cn/guide/practices/screens_support

歡迎關注:
我的掘金
我的簡書
我的CSDN
我的Github

五 主流機型

注: 以下機型的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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章