Android UI適配方案

大綱

  1. 使用dp而不是px
  2. 儘量使用自動適配佈局,而不要指定分辨率
  3. 使用寬高限定符
    1. values-1080x1920,以1080P爲基準計算每種常見分辨率對應的尺寸。
    2. 需要儘可能全的添加各種設備的分辨率(有工具)
    3. 容錯性不足,如果設備分辨率不能精確匹配對應限定符,會默認使用統一默認的dimens
  4. 第三方自動適配UI框架
    1. 原理:自定義RelativeLayout,在onMeasure中對控件分辨率做變換
    2. 第三方框架,維護性很成問題
    3. 一些自定義View,處理比較麻煩
  5. 最小寬度限定符,類似寬高限定符
    1. values-sw240dp,同樣以某一dp寬度爲基準計算其他寬度dp的值
    2. values-sw360dp、values-sw480dp
    3. 相比寬高限定符,最小寬度限定符不進行精確匹配,會遵循就近原則,可以較好的解決容錯問題。
    4. 如:設備寬364dp,系統會自動就近配置values-sw360dp下的dimens,顯示效果相差不會很大
  6. 今日頭條——修改density值
    1. 原理:px = dp x (dpi/160) = dp x density
    2. 既然如此,將density
    3. 需要UI出設計圖時以統一的dp爲基準
    4. https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA

基本概念

  • 像素——px
  • 密度獨立像素——dp或dip
  • 像素密度——dpi,單位面積內的像素數。
    • 軟件系統的概念。
    • 在系統出廠時,配置文件中的固定值。
    • 通常的取值有:160、240、360、480等。
    • 不同於物理概念上的屏幕密度ppi,如ppi爲415、430和470時,dpi可能會統一設置爲480。
  • density——當dpi=160時,1px = 1pd,此時denstiy的值爲1,dpi=240時,1.5px = 1dp,density的值爲1.5。
  • 上述值的關係:
    • denstiy = dpi / 160;
    • px = dp x density = dp x (dpi / 160)

Android設備的碎片化極爲嚴重,各種尺寸和分辨率的設備無比繁多。使得在Android開發中,UI適配變成了開發過程中極爲重要的一步。爲此Google提出了密度獨立像素dip或dp的概率,旨在更友好的處理Android UI適配問題。

但是效果嘛,只能說差強人意,可以解決大部分的業務場景,但是剩下的個別情況就搞死人了,原因在於Android設備碎片化實在太嚴重了,存在各種分辨率和dpi的設備。

比如兩臺設備A和B,分辨率是1920x1080,dpi分別爲420和480,在佈局中編寫一個100dp寬的ImageView,按照上面的公式ImageView的顯示寬度分別爲:100dp x 420 / 160 = 262.5100dp x 480 / 160 = 300,ImageView在B設備上明顯顯示要大一些。差異可能還不明顯,我們把寬度改爲360dp呢,A設備顯示寬度爲:948px,B設備顯示寬度爲:1080px。這就扯淡了,一個寬度填充滿屏幕,一個不滿。這種情況肯定是需要開發來背鍋解決的。

適配方案

雖然上面提到了使用dp無法解決全部業務場景,但是相對於直接使用px已經可以解決大部分場景下的適配問題了。

所以UI適配的第一條就是:

1. 使用dp代替px來編寫佈局。

又因爲上面無法適配的個別場景,所以UI適配的第二條是:

2.儘量使用自動適配佈局,而不要指定分辨率

這一條也很好理解,儘量使用ConstraintLayout 約束佈局和LinearLayout等父佈局,不要寫死分辨率,比如上面的例子如果使用match_parent而不是360dp,也可以避免出現顯示不一致問題(但是僅限於上列)。

限定符

Google同樣意識到dp滿足所以業務場景的需要,所以提供了寬度限定符的概念。

雖然您的佈局應始終通過拉伸其視圖內部和周圍的空間來應對不同的屏幕尺寸,但這可能無法針對每種屏幕尺寸提供最佳用戶體驗。例如,您爲手機設計的界面或許無法在平板電腦上提供良好的體驗。因此,您的應用還應提供備用佈局資源,以針對特定屏幕尺寸優化界面設計。

最小寬度限定符

使用“最小寬度”屏幕尺寸限定符,您可以爲具有最小寬度(以密度無關像素 dp 或 dip 爲度量單位)的屏幕提供備用佈局。

通過將屏幕尺寸描述爲密度無關像素的度量值,Android 允許您創建專爲非常具體的屏幕尺寸而設計的佈局,同時讓您不必對不同的像素密度有任何擔心。

通俗一點翻譯就是:可用通過xxxx-swXXXdp的方式定義一些最小限定符的資源文件,比如:values-sw400dp、values-sw600dp,系統會自動匹配如屏幕寬度相近資源文件夾。

我們再來看上面的例子兩臺設備A和B,分辨率是1920x1080,dpi分別爲360和400。我們簡化下問題比如設計圖給的是1920x1080 360dpi,包含一個22.5px * 22.5px = 10dp * 10dp的圖片。按經驗佈局應該如下編寫:

<ImageView
    android:id="@+id/img_iv"
    android:layout_width="10dp"
    android:layout_height="10dp"
    android:background="@mipmap/ic_launcher"/>

在不同設備上運行的結果:

  • 1280 x 720 240dpi的設備,圖片顯示爲15px * 15px;
  • 1920 x1080 360dpi的A設備,圖片顯示爲22.5px * 22.5px;
  • 1920 x1080 400dpi的B設備,圖片顯示爲25px * 25px;

可以看到B設備圖片顯示是有問題的,爲了解決這個問題,我們使用最小寬度限定符定義兩個資源文件夾:values-sw360dp和values-sw400dp。

在values-sw360dp中添加dimen.xml內容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="dp_1">1dp</dimen>
    <dimen name="dp_2">2dp</dimen>
    <dimen name="dp_3">3dp</dimen>
    <dimen name="dp_4">4dp</dimen>
    <dimen name="dp_5">5dp</dimen>
    <dimen name="dp_6">6dp</dimen>
    <dimen name="dp_7">7dp</dimen>
  <!-- 省略其他值 -->
  	<dimen name="dp_360">360dp</dimen>
    <!-- 因爲設計圖是360dpi,所以控件尺寸通常不會超過360dp,定義最大360dp的值足夠使用 -->
</resources>

在values-sw420dp中添加dimen.xml,文件中的dimen值很容易換算出來:在360dpi中dp_1 = 1dp,那麼在400dpi中dp_1 = 360 / 400 = 0.9dp,文件內容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="dp_1">0.9dp</dimen>
    <dimen name="dp_2">1.8dp</dimen>
    <dimen name="dp_3">2.7dp</dimen>
    <dimen name="dp_4">3.6dp</dimen>
    <dimen name="dp_5">4.5dp</dimen>
    <dimen name="dp_6">5.4dp</dimen>
    <dimen name="dp_7">6.3dp</dimen>
  	<!-- 省略其他值 -->
</resources>

注意要在values文件夾下添加默認dimen.xml,文件內容與values-sw360dp中添加dimen.xml一致(因爲設計圖恰好是360dpi的)。

佈局中的ImageView自然要改寫爲:

<ImageView
    android:id="@+id/img_iv"
    android:layout_width="@dimen/dp_10"
    android:layout_height="@dimen/dp_10"
    android:background="@mipmap/ic_launcher"/>

我們再來看一下不同設備運行結果:

  • 1280 x 720 240dpi的設備,未匹配到限定符使用values中的dimen,dp_10 = 10dp, px = 10 * 240 / 160 = 15px,圖片顯示尺寸爲15px * 15px。
  • 1920 x1080 360dpi的A設備,匹配到sw360dp限定符,dp_10 = 10dp, px = 10 * 360 / 160 = 20px,圖片顯示尺寸爲22.5px * 22.5px。
  • 1920 x1080 400dpi的B設備,匹配到sw420dp限定符,dp_10 = 9dp, px = 9 * 400 / 160 = 20px,圖片顯示尺寸爲22.5px * 22.5px。

完美的解決了設備A和B的顯示問題,所以UI適配的第三條是:

3. 使用最小(可用)寬度限定符,解決同樣分辨率不同dpi的設備適配問題。

這種方案看似完美,但是也有一些隱含的問題:此方案只能解決同樣分辨率不同dpi設備的適配問題:

  • 一旦出現不同分辨率相同dpi的情況就無效了(當然這種情況的可能性不高)。
  • 以上舉例只是基於1920x1080這一種分辨率爲例說明,試想一下如果1280x720的設備存在240dpi和280dpi的情況呢?我們只能針對特殊情況適配處理,無法解決全部場景適配問題。

寬高限定符

類似於上面說的最小寬度限定符,但是需要精確指定要匹配的設備寬高,values-1920x1080、values-1280x720等。配置與使用方式也與上面類似,如設計圖尺寸爲1920x1080 360dpi,那麼只需要以1920x1080爲基準計算所有分辨率對應的尺寸就可以了,佈局編寫時按照給的尺寸一一對應就可以,比如:給出的ImageView是20px*20px的,那在佈局中同樣指定width和height爲@dimen/dp_20就可以了。

values-1920x1080中dimens.xml如下:

<resources>
    <dimen name="dp_1">1px</dimen>
    <dimen name="dp_2">2px</dimen>
    <dimen name="dp_3">3px</dimen>
    <dimen name="dp_4">4px</dimen>
    <dimen name="dp_5">5px</dimen>
    <dimen name="dp_6">6px</dimen>
    <dimen name="dp_7">7px</dimen>
    <dimen name="dp_8">8px</dimen>
    <dimen name="dp_9">9px</dimen>
  	<!-- 省略其他 -->
  	<dimen name="dp_1920">1920px</dimen>
</resources>

values-1280x720中dimens.xml換算爲:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="dp_1">0.66px</dimen>
    <dimen name="dp_2">1.33px</dimen>
    <dimen name="dp_3">2.0px</dimen>
    <dimen name="dp_4">2.66px</dimen>
    <dimen name="dp_5">3.33px</dimen>
    <dimen name="dp_6">4.0px</dimen>
    <dimen name="dp_7">4.66px</dimen>
  	<!-- 省略其他 -->
</resources>

同樣需要在values添加默認尺寸dimen.xml,內容同基準分辨率文件。

因爲不是所有設備屏幕都是16:9的,也可以按照寬高拆分成兩個dimens.xml文件dimen_x.xml和dimen_y.xml,按照寬高:1920x1080分別換算得到x和y的值,但是頁面設計通常是豎屏可滑動的,所以對高度不敏感,只需要根據一個維度計算統一值就可以了。

以上計算方式比較簡單了,不需要自己編寫換算可以通過代碼工具或者自己寫個類實現。(網上有好多,找一下應該可以找到)。

理論上只要儘可能多的枚舉所有設備分辨率,就可以完美的解決屏幕適配問題,所以UI適配的第四條是:

4.使用寬高限定符,精確匹配屏幕分辨率。

這種方案已經近乎完美了,一度成爲比較熱門的解決方案,也有很多團隊使用過此方案。但是之前也說過Android設備的碎片化太嚴重了,綜合考慮基本不可能在項目中枚舉所有的屏幕尺寸進行適配,如果設備沒有匹配到對應尺寸會使用values下的默認尺寸文件,可能會出現嚴重的UI適配問題。

但是不可否認此種方案實現簡單,對於編寫佈局也很友好(直接填入設計圖的尺寸值就行,不需要換算),可以解決絕大多數的設備適配問題,是一種很友好的解決方案。

第三方UI適配框架

有很多第三方庫的解決方案,是從ViewGroup入手的,要麼重寫常用的如:RelativeLayout、LinearLayout和FrameLayout等在控件內部做轉換來適配不同尺寸的設備,要麼提供新的Layout如:Google的PercentLayout佈局。但是這些方案基本都不在維護了,這裏就不詳細展開了,感興趣的可以自行搜索瞭解。

UI適配的第五條是:

5. 使用第三方自適配框架,解決UI適配問題。

感興趣的可以參考以下文檔:

其他適配方案

參考字節的實現方案:

一種極低成本的Android屏幕適配方式

這篇文章着實屬於拾人牙慧了,起因是因爲看到了這篇博客Android 目前最穩定和高效的UI適配方案。所以想着確實應該把這部分知識梳理一下,所以寫了這篇文檔加了一些自己的裏面,主要也是爲了梳理知識點加深理解。

文中列舉的幾種UI適配方案,沒有嚴格的優劣之分,可以根據自己的業務需求選擇,也可以選擇幾種搭配使用,比如筆者目前主要做智能電視(盒子)的應用開發,Android電視不同於手機,碎片化沒有那麼嚴重,電視分辨率種類屈指可數,所以在日常項目中基本選擇使用寬高限定符的方案進行適配,效果也是極好的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章