1. 程式人生 > >給你一個全自動的屏幕適配方案(基於SW方案)!—— 解放你和UI的雙手

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

寬度 double 屏幕尺寸 高度 組件化 center ply mar 結束

技術分享圖片

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

前言

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

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

  1. 能完美適配UI稿。
  2. 適配完畢後,在高清設備上不會出現模糊的現象。
  3. 盡量減少對項目的侵入性。

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

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

Screen插件主要提供兩個功能:

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

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

如果要深入了解這個插件是如何自動幫你實現屏幕適配的,請仔細研讀下文。

本文的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 = 375dp的手機,我們可以看到sw為其他值的手機上面,顯示效果都不如意。在sw = 411dp和sw = 900dp的設備上,都留有大量的空白空間,而在sw = 360dp的設備上,則有超出屏幕範圍的現象。我們適配的目標就是:達到所有設備上顯示的效果都和設計稿(sw = 375dp上的效果)一致。

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

技術分享圖片

這裏有一點需要註意的是,可以看到第三臺設備裏面的適配還是有點問題,大概留下了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以下項目就可以看到生成的資源文件了,如下圖:

技術分享圖片

為了不影響編譯時間auto建議設置為false,需要的時候再手動啟動任務生成適配資源文件。

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

除了自動生成sw外,我們還需要確定,我們的App需要支持那些sw值。最簡單的方法就是,先確定我們要支持哪些設備。這裏筆者給出一個建議就是,市面上有非常多設備的sw值都是360dp的,所以我們必須要適配360dp的設備。至於其它的設備,我們可以這樣來確定,在開發者模式裏面找到一項叫做最小寬度的參數,裏面的值就是我們需要的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。下面我們來看看轉換效果:

轉換前

技術分享圖片

轉換後

技術分享圖片

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

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

註:目前版本不支持位圖刪除功能,所以當我們需要刪除部分位圖的時候,需要把自動生成的圖片文件全部刪除,重新生成,後續版本會增加該功能。

結語

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

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

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

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

微信公眾號:
如果你覺得這片文章對你有所啟發的話,可以關註我的微信公眾號哦
技術分享圖片

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