文章目錄
重要概念
px、pt、ppi、dpi、dp、sp 之間的關係
各自的定義
-
pt:point,點,印刷行業常用單位。
-
px:pixel,像素,電子屏幕上組成一幅圖畫或照片的最基本單元。
- 平常所說的 1920×1080 只是像素數量,也就是 1920px × 1080px,代表手機高度上有 1920 個像素點,寬度上有 1080 個像素點;1px 代表屏幕上一個物理的像素點。
- px 單位不被建議使用,因爲同樣 100px 的圖片,在不同手機上顯示的實際大小可能不同,如下圖所示(圖片來自android developer guide,下同)。
-
ppi:pixel per inch,每英寸像素數,該值越高,則屏幕越細膩。
-
dpi: dots per inch,每英寸多少點,該值越高,則圖片越細膩。
- 一般用 dpi 即每英寸多少像素來評價屏幕的顯示效果,dpi 等同 ppi 。
-
dp:Density-independent pixel, 是安卓開發用的長度單位。
- 1dp 表示在屏幕像素點密度爲 160ppi 時1px 長度。
- 其實 dp 就是爲了使得開發者設置的長度能夠根據不同屏幕(分辨率 / 尺寸也就是 dpi )獲得不同的像素 (px) 數量。比如:我將一個控件設置長度爲 1dp,那麼在 160dpi 上該控件長度爲 1px,在 240dpi 的屏幕上該控件的長度爲 240/160 = 1.5 px。也就是 dp 會隨着不同屏幕而改變控件長度的像素數量。
- dp 爲安卓開發時的長度單位,根據不同的屏幕分辨率,與 px 有不同的對應關係。安卓端屏幕大小各不相同,根據其像素密度,分爲以下幾種規格(圖片來自網絡):
- 1dp 定義爲屏幕密度值爲 160ppi 時所對應的像素爲 1px,即,在 mdpi 時,1dp = 1px。
以 mdpi 爲標準,這些屏幕的密度值比爲:
即,在 xhdpi 的密度下,1dp = 2px;在hdpi情況下,1dp = 1.5px;其他類推。
其實記住一點,dp 最終都要化爲像素數量來衡量大小的,因爲只有像素數量最直觀。使用 dp 作爲單位在不同手機上的效果, 見下圖。(圖片來自android developer guide)。
- dip:與 dp 完全相同,只是名字不同而已。在早期的 Android 版本里多使用 dip,後來爲了與 sp 統一就建議使用 dp 這個名字了。
- sp: scale-independent pixel,安卓開發用的字體大小單位。
- 與縮放無關的抽象像素(Scale-independent Pixel)。sp 和 dp 很類似但唯一的區別是,使用 sp 作爲字體單位時,Android 系統允許用戶自定義文字尺寸大小(小、正常、大、超大等等),當文字尺寸是“正常”時,1sp = 1dp = 0.00625 英寸,而當文字尺寸是“大”或“超大”時,1sp > 1dp = 0.00625英寸。類似我們在 windows 裏調整字體尺寸以後的效果——窗口大小不變,只有文字大小改變。
換算公式
- 1pt = (dpi / 72) px
- dpi = ppi (實際上是約等於)
- ppi = 屏幕對角線上的像素點數/對角線長度 = √ (屏幕橫向像素點^2 + 屏幕縱向像素點^2)/ 對角線長度
- 1dp =(dpi / 160)px
- 當文字尺寸是“正常”時 1sp = 1dp,而當文字尺寸是“大”或“超大”時,1sp > 1dp。一般情況下可認爲sp = dp。
現象剖析
在標識尺寸的時候,Android 並不推薦我們使用 px 這個真實像素單位,因爲不同的手機之間,分辨率是不同的,比如一個 96*96 像素的控件在分辨率越來越高的手機上會在整體 UI 中看起來越來越小。(圖片來自android developer guide,下同)。
核心問題
Android適配最核心的問題有兩個:
- 適配的高效率,即把設計圖轉化爲 App 界面的過程是否高效;
- 高度的兼容性,如何保證實現 UI 界面在不同尺寸和分辨率的手機中 UI 的一致性。
這兩個問題都很重要,一個是保證我們開發的高效,一個是保證我們適配的成效;今天我們就這兩個核心的問題來聊一聊Android的適配方案。
適配方案
直接適配
原理
dpi 是像素密度,指的是在系統軟件上指定的單位尺寸的像素數量,它往往是寫在系統出廠配置文件的一個固定值。
大家買手機的時候,往往會聽到另一個叫 ppi 的參數,這個在手機屏幕中指的也是像素密度,但是這個是物理上的概念,它是客觀存在的不會改變。dpi 是軟件參考了物理像素密度後,人爲指定的一個值,這樣保證了某一個區間內的物理像素密度在軟件上都使用同一個值。這樣會有利於我們的 UI 適配。
Android 推薦使用 dp 作爲尺寸單位來適配 UI。理由是 ,dp 指的是設備獨立像素密度,以 dp 爲尺寸單位的控件,在不同分辨率和尺寸的手機上代表了不同的真實像素。根據上面的概念,我們都知道一個公式:
系統都是通過這個來判斷 px 和 dp 的數學關係。 這裏的 dpi 也就是 ppi(ppi 它是一個物理概念,表示的是每英寸的像素點)。
而在不同分辨率下,dpi 將會不同,比如:
… | 1080*720 | 1920*1080 |
---|---|---|
dpi | 320 | 480 |
dpi/160 | 2 | 3 |
根據上面的表格,我們可以發現,720P 和 1080P 的手機,dpi 是不同的,這也就意味着,不同的分辨率中,1dp 對應不同數量的 px ( 720P 中,1dp = 2px,1080P 中 1dp = 3px ),這就實現了,當我們使用 dp 來定義一個控件大小的時候,他在不同的手機裏表現出相應大小的像素值。也就在不同的手機裏面表現一致(圖片來自android developer guide,下同)。
優點
- 最原始的適配方案
- 我們可以說,通過 dp 加上自適應佈局和 weight 比例佈局可以基本解決不同手機上適配的問題,這基本是最原始的 Android 適配方案。
缺點
- 只能適配 90% 的手機
- 這隻能保證我們寫出來的界面適配絕大部分手機,部分手機仍然需要單獨適配,爲什麼 dp 只解決了 90% 的適配問題,因爲並不是所有的 1080P 的手機 dpi 都是 480,比如 Google 的 Pixel2(1920 x 1080)的 dpi 是 441,也就是說,在 Pixel2 中,1dp = 2.756px, 這樣會導致相同分辨率的手機中,這樣,一個 100dp x 100dp 的控件,在一般的 1080P手機上,可能都是 300px, 而 Pixel 2 中 ,就只有 275.6px ,這樣控件的實際大小會有所不同。
- 效率低
- 這種方式無法快速高效的把設計師的設計稿實現到佈局代碼中。設計稿的 ImageView 是 128px * 128px,當我們在編寫 layout 文件的時候,卻不能直接寫成 128dp * 128dp。在把設計稿向UI代碼轉換的過程中,我們需要耗費相當的精力去轉換尺寸,這會極大的降低我們的生產力,拉低開發效率(雖然通常來說,我們寫程序時幾乎不用計算 dp,而是直接憑着自己的感覺寫的)。
寬高限定符適配(分辨率限定符適配)
原理
也可以看這裏的講解
簡單說,就是窮舉市面上所有的Android手機的寬高像素值:
設定一個基準的分辨率,其他分辨率都根據這個基準分辨率來計算,在不同的尺寸文件夾內部,根據該尺寸編寫對應的dimens 文件。
比如以 480x320 爲基準分辨率
- 寬度爲320,將任何分辨率的寬度整分爲320份,取值爲 x1-x320
- 高度爲480,將任何分辨率的高度整分爲480份,取值爲 y1-y480
那麼對於 800*480 的分辨率的 dimens 文件來說,
x1 = ( 480 / 320 ) * 1 = 1.5px
x2 = ( 480 / 320 ) * 2 = 3px
…
優點
- 提升了開發效率
- 這個時候,如果我們的 UI 設計界面使用的就是基準分辨率,那麼我們就可以按照設計稿上的尺寸填寫相對應的 dimens 引用了,而當 APP 運行在不同分辨率的手機中時,這些系統會根據這些 dimens 引用會去該分辨率的文件夾下面尋找對應的值。這樣基本解決了我們的適配問題,而且極大的提升了我們UI開發的效率。
- 有團隊使用過,是一個成熟的適配方案。
缺點
- 容錯機制很差
但是這個方案有一個致命的缺陷,那就是需要精準命中才能適配,比如 1920x1080 的手機就一定要找到1920x1080 的限定符,否則就只能用統一的默認的 dimens 文件了。而使用默認的尺寸的話,UI就很可能變形,簡單說,就是容錯機制很差。 - 容易變形
因爲同時採用寬和高的適配,所以面對市面上紛繁的寬高比不同的手機,UI 會嚴重變形。比如 16:9,18:9的屏幕,效果是不一樣的,全面屏出來之後,這種方案不適用。
備註
鴻洋大佬的適配方案的項目也來自於寬高限定符方案的啓發。
優點
這可以說是一個極好的方案,因爲它在寬高限定符適配的基礎上更進一步,並且解決了容錯機制的問題,可以說完美的達成了開發高效和適配精準的兩個要求。
缺點
- 但是我們能夠想到,因爲框架要在運行時會在 onMeasure 裏面做變換
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (!isInEditMode())
{
mHelper.adjustChildren();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
-
我們自定義的控件可能會被影響或限制,可能有些特定的控件,需要單獨適配,這裏面可能存在的暗坑是不可預見的,還有一個比較重要的問題,那就是整個適配工作是有框架完成的,而不是系統完成的,一旦使用這個框架,未來一旦遇到很難解決的問題,替換起來是非常麻煩的,而且項目一旦停止維護,後續的升級就只能靠你自己了,這種代價團隊能否承受?當然,它已經停止維護了。
-
另外,這個方案是根據寬高比例進行適配的,在當前 Android 碎片化嚴重的情況下,各家廠商屏幕寬高比有很大的不同,這樣會造成嚴重的變形。
SmallestWidth 適配
原理
也可以看這裏的一篇講解
或者叫 sw 限定符適配。指的是 Android 會識別屏幕可用高度和寬度的最小尺寸的 dp 值(其實就是手機的寬度值),然後根據識別到的結果去資源文件中尋找對應限定符的文件夾下的資源文件。
這種機制和上文提到的寬高限定符適配原理上是一樣的,都是系統通過特定的規則來選擇對應的文件。
舉個例子,小米 5 的 dpi 是 480,橫向像素是 1080px,根據
橫向的 dp 值是 1080 / ( 480 / 160 ),也就是 360dp,系統就會去尋找是否存在 value-sw360dp 的文件夾以及對應的資源文件。
優點
- 解決了上文提到的寬高限定符的容錯問題
- smallestWidth 限定符適配和寬高限定符適配最大的區別在於,前者有很好的容錯機制,如果沒有 value-sw360dp文件夾,系統會向下尋找,比如離 360dp 最近的只有 value-sw350dp,那麼Android就會選擇 value-sw350dp 文件夾下面的資源文件。這個特性就完美的解決了上文提到的寬高限定符的容錯問題。
- 開發效率高
- 這套方案是上述幾種方案中最接近完美的方案。首先,從開發效率上,它不遜色於上述任意一種方案。根據固定的放縮比例,我們基本可以按照 UI 設計的尺寸不假思索的填寫對應的 dimens 引用。 我們還有以 375 個像素寬度的設計稿爲例,在 values-sw360dp 文件夾下的 diemns 文件應該怎麼編寫呢?這個文件夾下,意味着手機的最小寬度的dp值是 360,我們把 360dp 等分成 375 等份,每一個設計稿中的像素,大概代表 smallestWidth 值爲360dp 的手機中的 0.96dp,那麼接下來的事情就很簡單了,假如設計稿上出現了一個10px*10px 的 ImageView,那麼,我們就可以不假思索的在layout文件中寫下對應的尺寸。
而這種 diemns 引用,在不同的 values-swdp 文件夾下的數值是不同的,比如 values-sw360dp 和 values-sw400dp,
當系統識別到手機的 smallestWidth 值時,就會自動去尋找和目標數據最近的資源文件的尺寸。
- 穩定性更好
- 其次,從穩定性上,它也優於上述方案。原生的 dp 適配可能會碰到 Pixel 2 這種有些特別的手機需要單獨適配,但是在 smallestWidth 適配中,通過計算 Pixel 2 手機的的 smallestWidth 的值是 411,我們只需要生成一個values-sw411dp(或者取整生成 values-sw410dp 也沒問題)就能解決問題。
- smallestWidth 的適配機制由系統保證,我們只需要針對這套規則生成對應的資源文件即可,不會出現什麼難以解決的問題,也根本不會影響我們的業務邏輯代碼,而且只要我們生成的資源文件分佈合理,即使對應的smallestWidth 值沒有找到完全對應的資源文件,它也能向下兼容,尋找最接近的資源文件。
缺點
- 增加 apk 的體積
- 多個 dimens 文件可能導致 apk 變大,這是事實,根據生成的 dimens 文件的覆蓋範圍和尺寸範圍,apk 可能會增大 300kb-800kb 左右,我認爲這是可以接受的。
小工具
生成 values-sw 的項目代碼,點擊這裏獲取項目地址
今日頭條適配方案
原理
也可以看這裏的講解
今日頭條屏幕適配方案的核心原理在於,根據以下公式算出 density:
設計圖總寬度計算公式:
dpi 的獲取方式,系統已經提供了
DisplayMetrics dm = new DisplayMetrics();
int dpi = dm.densityDpi
density 的意思就是 1 dp 佔當前設備多少像素
只要 density 根據不同的設備進行實時計算並作出改變,就能保證 設計圖總寬度 不變,也就完成了適配
舉例說明
比如,設計稿寬度是 360px,那麼開發這邊就會把目標 dp 值設爲 360dp,在不同的設備中,動態修改 density 值,從而保證(手機像素寬度) px / density 這個值始終是 360dp,這樣的話,就能保證 UI 在不同的設備上表現一致了。至於具體是怎麼修改的,可以看看源碼以及下面升級版的文章。
升級版 (AndroidAutoSize)
升級版是 github 開源作者在今日頭條技術團隊提出 30 多行核心適配代碼的基礎上,擴展到了十幾個類,使得適配效率和兼容性大大提高了,這裏感謝原作者的辛苦付出和開源精神,推薦大家使用這個升級版。
優點
-
穩定性好,侵入性低
- 這個方案侵入性很低,而且也沒有涉及私有 API,是極不錯的方案,有今日頭條的大廠在用,穩定性應當是有保證的。
-
可以快速接入,替換方案時成本很低