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。

image

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區域劃分
image
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)。

真機數據未適配前:

image
未適配前:
小米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的位置就會跟設計圖設計的有很大的差距,這種差距是隨著手機豎直分辨率的增大而增大。

真機適配后:

image
高度適配后:
小米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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內容