給你一個全自動的螢幕適配方案(基於SW方案)!—— 解放你和UI的雙手

Tangpj發表於2018-09-28

原文地址:tangpj.com/2018/09/29/…

簡書地址:www.jianshu.com/p/6824ae172…

Calces系列相關文章:Calces自動實現Android元件化模組構建

前言

螢幕適配一直是移動端開發熱議的問題,但是適配方案往往在實際開發的時候會和UI提供的設計稿衝突。本文主要是基於官方推薦的配置限定符方案(Smallest Width目前Android螢幕適配的最優方案)來實現一個接近完美的螢幕適配方案。

原創宣告: 該文章為原創文章,未經博主同意嚴禁轉載。

對於完美的適配方案筆者是這樣定義的:

  1. 能完美適配UI稿。
  2. 適配完畢後,在高清裝置上不會出現模糊的現象。
  3. 儘量減少對專案的侵入性。

下面我會從螢幕適配的一些基礎知識入手,向你慢慢展現一個最優的螢幕適配方案。

這是我寫的Android構建輔助外掛庫,其中的Screen外掛是實現自動螢幕適配的關鍵。因為怕大家錯過這個外掛,所以在這裡提前推薦給大家。

Screen外掛主要提供兩個功能:

  1. 配置設計稿密度與需要適配螢幕的Smallest Width值來自動生成對應的資原始檔
  2. 提供需要的最高清的點陣圖,根據需要縮放的密度自動縮放點陣圖資源。

Github: 如果覺得這個工具對您有幫助的話,可以點下Star,這是我堅持下去的動力?

如果要深入瞭解這個外掛是如何自動幫你實現螢幕適配的,請仔細研讀下文。

本文的Demo地址:DEMO。專案中的ScreenAdaptation就是本文的Demo。

螢幕適配概覽

概念

  • 螢幕尺寸: 螢幕尺寸是指螢幕的物理尺寸,是通過測量螢幕的對角線測量出來的。

  • 螢幕密度: 螢幕物理區域中的畫素量,通常稱為dpi(每英寸的畫素點數)。密度越高,現實效果越好。

  • 解析度: 螢幕上物理畫素的總數。在進行螢幕適配時,不要直接通過解析度適配,應該通過螢幕尺寸和螢幕密度來適配

  • dp: dp是Android特有的虛擬畫素單位,與物理引數無關。1dp等於160 dpi螢幕上的一個物理畫素,在執行時,系統 根據使用中螢幕的實際密度按需要以透明方式處理 dp 單位的任何縮放 。dp 單位轉換為螢幕畫素很簡單: px = dp * (dpi / 160)。在 240 dpi 螢幕上,1 dp 等於 1.5 物理畫素。

如何支援多種螢幕

Android支援多種螢幕的基礎是它能夠針對當前螢幕的配置,以適當的方式渲染應用的佈局和點陣圖,這是由系統層面提供的支援。我們可以通過以下方式來更好地處理不同螢幕配置的適配:

  1. 為不同的螢幕尺寸提供不同的佈局 預設情況下,Android會調整應用的佈局大小以適應當前裝置的螢幕,大多數情況下系統提供的支援就能滿足我們的需要。但是有時候需要針對不同的螢幕解析度來設計不同的佈局,以達到更好的現實效果。
  2. 為不同的螢幕密度提供不同的圖片資源 我們可以通過配置密度資源的配置限定符來提供不同畫素的圖片,來適配不同的螢幕密度。

對於第一點,在實際工作中是很難實現的。因為一般UI只會提供一套設計稿,不會根據不同解析度的螢幕來提供相應的適配。但是我們沒辦法控制我們的App最終會執行在什麼解析度的螢幕上,為了達到在不同螢幕上的顯示效果一直,我們可以通過提供不同密度的點陣圖資源與Smallest Width方案來實現螢幕適配。

什麼是Smallest Width適配

Smallest Width字面上的意思就是最小寬度,由可用螢幕區域的最小尺寸指定。 具體來說,裝置的 smallestWidth 是螢幕可用高度和寬度的最小尺寸。

例如,如果佈局要求螢幕區域的最小尺寸始終至少為 600 dp,則可使用此限定符建立佈局資源 res/layout-sw600dp/。僅當可用螢幕的最小尺寸至少為 600dp 時,系統才會使用這些資源,而不考慮 600dp 所代表的邊是使用者所認為的高度還是寬度。smallestWidth 是裝置的固定螢幕尺寸特性;裝置的 smallestWidth 不會隨螢幕方向的變化而改變

所以我們可以根據需要適配的螢幕的sw值來提供不同的資源來實現螢幕適配。

UI設計與螢幕適配的一些基礎理念

我覺得很多螢幕適配教程都漏了一個很重要的點,就是:沒有解釋清楚螢幕適配與UI設計之間的關係!

一般在實際開發的時候,UI設計師都會提供一套UI稿與標尺,工程師是通過這套標尺來開發UI的。UI如果我們要做好Android的螢幕適配,那麼我們必須要明白的一點就是,UI稿在我們進行介面開發中是充當錨點的作用的。要適配其它的螢幕的話,必須要以這個基準為基礎計算其它螢幕的dimens資源的值。

舉個例子: 例如,很多UI設計師都會以iPhone6的尺寸作為標準來製作設計稿與標尺的,而iPhone6的螢幕寬度為375px,所以這個寬度為375px的設計稿就是我們螢幕適配的基準了。

假設有一臺sw等於375dp的裝置的話,那麼這個裝置與設計稿對應的關係就是1dp = 1px,那麼我們就不需要進行任何適配,直接把設計稿以px為單位的標尺值以1:1的比例轉換成以dp為單位就可以了。

在這裡,我們可以得出一個結論就是:螢幕適配需要以UI稿為基準再製定合適的適配方案!

但是有一個問題就是,每個UI設計師的喜好都是不一樣的,提供的設計稿的比例尺也不是固定的。而且Android的螢幕碎片化非常嚴重,我們需要適配的螢幕的sw的值也是變化多端的。所以如果每次都需要手動計算對應的dimens值的話,非常耗時間與繁瑣。網上提供了一些工具來快速生成對應sw的dimens值,但是這些工具都會存在兩個缺點:

  1. 沒辦法根據UI設計稿來轉換,所以不一定能100%還原設計稿效果

  2. 會生成大量無用的dimens值。其實如果我們細心觀察過設計稿的話,我們會發現,其實每份設計稿常用的px值都是固定的十來個。例如同樣以375px的設計稿為基準的話,使用工具會生成1px ~ 375px對應的dp值,所以會存在大量的無用dimens值。這樣只會徒增安裝包的大小。

這個兩個缺點,可以使用筆者的calces.screen外掛來解決,下文會介紹這個外掛的使用方法與使用效果的。

使用calces.screen快速實現Smalles Widths適配方案

適配前與適配後對比情況

還是以iPhone6的設計稿為例子,假如有下面這麼一副設計稿,如果不進行任何適配的話,在不同的裝置上的顯示效果對比如下:

給你一個全自動的螢幕適配方案(基於SW方案)!—— 解放你和UI的雙手

第一個手機就是上文中說到的sw = 375dp的手機,我們可以看到sw為其他值的手機上面,顯示效果都不如意。在sw = 411dp和sw = 900dp的裝置上,都留有大量的空白空間,而在sw = 360dp的裝置上,則有超出螢幕範圍的現象。我們適配的目標就是:達到所有裝置上顯示的效果都和設計稿(sw = 375dp上的效果)一致。

使用calces.screen外掛適配後的效果如圖所示:

給你一個全自動的螢幕適配方案(基於SW方案)!—— 解放你和UI的雙手

這裡有一點需要注意的是,可以看到第三臺裝置裡面的適配還是有點問題,大概留下了1dp左右的白邊。這個是pixel 2 XL的模擬器,可以看到,測量出來的sw值應該是411dp的,但是經過筆者的實際測量,發現sw應該是412dp才對。有興趣的讀者可以自己在佈局編輯器裡面建立一個width為411dp的控制元件,可以看到在pixel 2 XL裝置下也是有大概1dp的白邊的。所以這個1dp的誤差應該是和裝置有關的,這裡貼上用calces.screen生成的sw = 411dp的dimens檔案的值觀大家參考。

<resources>
  <!-- sw411dp -->
  <dimen name='px_48'>53dp</dimen>
  <dimen name='px_75'>83dp</dimen>
  <dimen name='px_100'>110dp</dimen>
  <dimen name='px_125'>137dp</dimen>
  <dimen name='px_150'>165dp</dimen>
  <dimen name='px_200'>220dp</dimen>
  <dimen name='px_250'>274dp</dimen>
  <dimen name='px_300'>329dp</dimen>
  <dimen name='px_375'>411dp</dimen>
  <dimen name='text_px_28'>31sp</dimen>
  <dimen name='text_px_32'>36sp</dimen>
  <dimen name='text_px_40'>44sp</dimen>
</resources>
複製程式碼

當sw = 411dp 時,px_375的實際值時411dp,所以這是符合我們的預期轉換結果的。

如何引入calces.screen

首先,我們需要引入calces外掛,引入的方式很簡單:

在專案的build.gradle中新增程式碼:

//Gradle版本高於2.1的情況下(推薦方案)
plugins {
    id "calces.screen" version "1.2.3"
}

//Gradle版本低於2.1的情況下(2.1以上版本也相容這種方式)
buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "gradle.plugin.com.tangpj.tools:calces:1.2.3"
  }
}

複製程式碼

在modules的build.gradle中新增程式碼:

apply plugin: "calces.appconfig"
複製程式碼

使用calces.screen適配螢幕

首先,我們需要在res/values/資料夾中建立dimens.xml檔案,然後按照設計稿的標尺把需要用到的尺寸寫到該檔案下。例如:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--design 375px-->
    <dimen name="px_48">48dp</dimen>
    <dimen name="px_75">75dp</dimen>
    <dimen name="px_100">100dp</dimen>
    <dimen name="px_125">125dp</dimen>
    <dimen name="px_150">150dp</dimen>
    <dimen name="px_200">200dp</dimen>
    <dimen name="px_250">250dp</dimen>
    <dimen name="px_300">300dp</dimen>
    <dimen name="px_375">375dp</dimen>

    <!--text size-->
    <dimen name="text_px_28">28sp</dimen>
    <dimen name="text_px_32">32sp</dimen>
    <dimen name="text_px_40">40sp</dimen>
</resources>
複製程式碼

這就是我們的基準dimens檔案。

現在我們只需要把基準尺寸與需要適配的尺寸通過Gradle配置就可以了,例如,上面的例子中,我們需要適配的sw有:320dp, 411dp, 900dp,那麼我們需要在modules的build.gradle檔案下新增如下程式碼:

screen{

    dimens{
        designPx 375
        smallesWidths 320,375,411,900
        scale BigDecimal.ROUND_UP
        auto true
    }

}
複製程式碼

上面配置資訊的對應關係是:

  • designPx:設計稿的sw尺寸(單位px)

  • smallesWidths:需要適配的螢幕sw尺寸(單位dp)

  • scale: 數字取整的方式 因為Android系統只能適配整數單位的dp值,所以我們可以通過scale來配置具體的取正方式。這裡直接取BigDecimal提供的round來實現。如果不設定的話,則會生成double型別的dp值(實際使用的時候會丟棄小數位)

  • auto:是否自動生成dimens,當auto為true時,每次build都會重新生成一次適配dimens檔案。 如果不設定auto或設定為false的話,可以手動呼叫gradle任務來生成。 呼叫命令: /gradlew dimensCovert 也可以直接點選gradle任務執行,方式如下圖:

配置完畢後,重新build以下專案就可以看到生成的資原始檔了,如下圖:

給你一個全自動的螢幕適配方案(基於SW方案)!—— 解放你和UI的雙手

為了不影響編譯時間auto建議設定為false,需要的時候再手動啟動任務生成適配資原始檔。

如何確定我們需要適配什麼sw值?

除了自動生成sw外,我們還需要確定,我們的App需要支援那些sw值。最簡單的方法就是,先確定我們要支援哪些裝置。這裡筆者給出一個建議就是,市面上有非常多裝置的sw值都是360dp的,所以我們必須要適配360dp的裝置。至於其它的裝置,我們可以這樣來確定,在開發者模式裡面找到一項叫做最小寬度的引數,裡面的值就是我們需要的sw值。具體如下圖:

Nexus S sw值

例如,上面這個是Nexus S的sw值。如果我們不專門適配sw = 384dp的螢幕的話,那麼系統就會預設尋找低於384dp的適配資源(所以360dp是一個相對通用的適配值)。當我們擁有測試裝置的時候,使用calces.screen適配是非常簡單的。那麼如果我們不知道沒有測試裝置呢?(例如有使用者反饋,某個裝置下的適配有很大問題)

這裡給大家推薦一個網站:Device Metrics

這個網站是Material Design的裝置引數查詢網站,使用者在這裡直接找到對應裝置的尺寸就可以了(之前的方法翻車了,溜了溜了)。

一般情況下,sw為360dp和480dp的螢幕會比較常見,所以我們必須要生成這兩套資源,如果需要支援Pad的話,則需要適配sw = 600dp 或 sw = 720dp的螢幕,然後再根據實際情況適配其它sw值的螢幕。

到這裡為止,我們就完成了Android基於sw方案的螢幕適配了,非常簡單!

但是,本文還沒結束,這個外掛除了提供自動實現基於sw方案的適配外,還提供了一個殺手級功能:根據配置自動把生成對應解析度的點陣圖資源。當我們需要適配多種不同螢幕密度的手機的時候,只需要提供一套高清點陣圖資源就可以了,解放你和UI設計師的雙手。

calces.screen實現點陣圖自動縮放適配

為不同密度的螢幕提供不同的點陣圖資源是Google官方推薦的螢幕適配做法。這樣做的好處是,能使App在不同密度的螢幕上都能達到最好的效果,不會出現在高清屏下出現老年機的顯示效果,並且在不同密度的螢幕下都能保持相對穩定的顯示效果。下面是點陣圖資源密度對應的比例關係:

密度限定符 比例關係 說明
ldpi 0.75 適用於低密度螢幕 (~120dpi) 的資源
mdpi 1 適用於中密度螢幕 (~160dpi) 的資源(基線密度)
hdpi 1.5 適用於高密度螢幕 (~240dpi) 的資源
xhdpi 2 適用於超高密度螢幕 (~320dpi) 的資源
xxhdpi 3 適用於超超高密度螢幕 (~480dpi) 的資源
xxxhdpi 4 適用於超超超高密度螢幕 (~640dpi) 的資源。此限定符僅適用於 啟動器圖示。

但是這裡會產生一個問題,一般情況下,點陣圖資源是UI設計師提供給我們的。我和很多UI設計師討論過,他們的方案就是先切一套最高清的圖片,然後再根據需要進行縮放,然後提供給工程師使用。

一般情況下,這種做法除了繁瑣點也沒什麼問題。但是如果現在出現了一個情況,就是需要支援更低密度的螢幕呢?這種情況只能讓UI設計師再縮放一套密度的點陣圖。那如果某部分點陣圖已經不再使用了,需要刪除呢?那工程師需要把其它密度的點陣圖找出來再刪除。而且再往工程裡面新增新的點陣圖的時候也需要手工新增。

所以一般情況下,UI提供圖片資源 —— 工程師使用圖片資源這個過程中是純手工控制的。工作非常繁瑣並且沒什麼意義,而且手動遷移的過程中還非常容易出錯(想想如果複製漏了某幾個密度的點陣圖資源會是什麼畫面?)。所以calces.screen還提供了點陣圖管理功能。

calces.screen管理點陣圖

使用Screen的點陣圖縮放功能之前,先和設計師/產品商量好App最高需要支援哪個密度的螢幕。然後設計師以後只需要提供這套密度的點陣圖就可以了。之後我們只需要在modules的build.gradle中進行配置,配置方式如下:

screen{
    mipmap{
        designDensity "xxxhdpi" //測試用,目前手機螢幕最高只支援到xxhdpi
        mipmapDensity 'xxhdpi','xhdpi','hdpi','mdpi'
        auto true
    }

}
複製程式碼

配置完之後,重新build檔案就可以了,當然不希望增加編譯時間的話,可以把auto置為false或者不設定。mipmap支援增量編譯功能,只會對資料夾中不存在的點陣圖進行縮放,已存在則跳過,識別條件是檔名。手動啟動點陣圖縮放功能的方式和上述方式一致,任務名稱是mipmapZoom。下面我們來看看轉換效果:

轉換前

給你一個全自動的螢幕適配方案(基於SW方案)!—— 解放你和UI的雙手

轉換後

給你一個全自動的螢幕適配方案(基於SW方案)!—— 解放你和UI的雙手

讀者可以點進去檢視一下轉換後的圖片尺寸,可以發現,轉換後的圖片符合我們需要的的比例。有興趣的讀者可以下載demo,把其它解析度的點陣圖資源刪除,通過mipmapZoom任務重新生成。

通過mipmapZoom任務,可以大大減少UI設計師與工程師的工作量,只需要管理一套點陣圖檔案即可,把我們從機械化的任務中解放出來。

注:目前版本不支援點陣圖刪除功能,所以當我們需要刪除部分點陣圖的時候,需要把自動生成的圖片檔案全部刪除,重新生成,後續版本會增加該功能。

結語

螢幕適配一直是移動端開發工程師的一大難題,面對琳琅滿目的螢幕尺寸與螢幕密度,我們一直在找一個更好的適配方案。Smallest Width是目前Android中最簡單最好用的適配方案,沒有之一,它是由系統提供支援的,並且在適配時不會因為螢幕解析度與設計稿的差異過大造成一些奇奇怪怪的問題(大螢幕上面變糊,小螢幕又顯得畫素過於密集)。筆者這個適配方案是基於Smallest Width與提供多套點陣圖為基礎,通過Gradle外掛來自動處理sw比例計算與檔案生成、點陣圖自動縮放來實現一個相對更好的適配方案。

calces.screen開發的初衷時簡化UI設計師與Android工程師的工作量,目前已經基本達城這一目標。

好了,關於calces.screen外掛的介紹就到此為止了,這裡再一次提醒大家,如果覺得calces對你有所幫助的話,可以點下star,鼓勵下作者。如果有一些更好的想法的話,可以參與這一開源專案。筆者會一直維護這個專案的,致力於減輕Android工程師的負擔,把重複的機械性工作全部交給Gradle來處理。

Github: 如果覺得這個工具對您有幫助的話,可以點下Star,這是我堅持下去的動力?

相關文章