Calces系列相關(guān)文章:Calces自動實(shí)現(xiàn)Android組件化模塊構(gòu)建
前言
屏幕適配一直是移動端開發(fā)熱議的問題铺坞,但是適配方案往往在實(shí)際開發(fā)的時候會和UI提供的設(shè)計稿沖突靠粪。本文主要是基于官方推薦的配置限定符方案(Smallest Width目前Android屏幕適配的最優(yōu)方案)來實(shí)現(xiàn)一個接近完美的屏幕適配方案聊疲。
原創(chuàng)聲明: 該文章為原創(chuàng)文章,未經(jīng)博主同意嚴(yán)禁轉(zhuǎn)載搭儒。
對于完美的適配方案筆者是這樣定義的:
- 能完美適配UI稿穷当。
- 適配完畢后,在高清設(shè)備上不會出現(xiàn)模糊的現(xiàn)象淹禾。
- 盡量減少對項目的侵入性馁菜。
下面我會從屏幕適配的一些基礎(chǔ)知識入手,向你慢慢展現(xiàn)一個最優(yōu)的屏幕適配方案稀拐。
這是我寫的Android構(gòu)建輔助插件庫火邓,其中的Screen插件是實(shí)現(xiàn)自動屏幕適配的關(guān)鍵丹弱。因為怕大家錯過這個插件德撬,所以在這里提前推薦給大家。
Screen插件主要提供兩個功能:
- 配置設(shè)計稿密度與需要適配屏幕的Smallest Width值來自動生成對應(yīng)的資源文件
- 提供需要的最高清的位圖躲胳,根據(jù)需要縮放的密度自動縮放位圖資源蜓洪。
如果要深入了解這個插件是如何自動幫你實(shí)現(xiàn)屏幕適配的隆檀,請仔細(xì)研讀下文。
本文的Demo地址:https://github.com/Tangpj/Android-advanced-blueprint粹湃。項目中的ScreenAdaptation就是本文的Demo恐仑。
屏幕適配概覽
概念
屏幕尺寸: 屏幕尺寸是指屏幕的物理尺寸,是通過測量屏幕的對角線測量出來的为鳄。
屏幕密度: 屏幕物理區(qū)域中的像素量裳仆,通常稱為dpi(每英寸的像素點(diǎn)數(shù))。密度越高孤钦,現(xiàn)實(shí)效果越好歧斟。
分辨率: 屏幕上物理像素的總數(shù)。在進(jìn)行屏幕適配時偏形,不要直接通過分辨率適配静袖,應(yīng)該通過屏幕尺寸和屏幕密度來適配
dp: dp是Android特有的虛擬像素單位,與物理參數(shù)無關(guān)俊扭。1dp等于160 dpi屏幕上的一個物理像素队橙,在運(yùn)行時,系統(tǒng) 根據(jù)使用中屏幕的實(shí)際密度按需要以透明方式處理 dp 單位的任何縮放 。dp 單位轉(zhuǎn)換為屏幕像素很簡單: px = dp * (dpi / 160)捐康。在 240 dpi 屏幕上畅姊,1 dp 等于 1.5 物理像素。
如何支持多種屏幕
Android支持多種屏幕的基礎(chǔ)是它能夠針對當(dāng)前屏幕的配置吹由,以適當(dāng)?shù)姆绞戒秩緫?yīng)用的布局和位圖若未,這是由系統(tǒng)層面提供的支持。我們可以通過以下方式來更好地處理不同屏幕配置的適配:
- 為不同的屏幕尺寸提供不同的布局
默認(rèn)情況下倾鲫,Android會調(diào)整應(yīng)用的布局大小以適應(yīng)當(dāng)前設(shè)備的屏幕粗合,大多數(shù)情況下系統(tǒng)提供的支持就能滿足我們的需要。但是有時候需要針對不同的屏幕分辨率來設(shè)計不同的布局乌昔,以達(dá)到更好的現(xiàn)實(shí)效果隙疚。 - 為不同的屏幕密度提供不同的圖片資源
我們可以通過配置密度資源的配置限定符來提供不同像素的圖片,來適配不同的屏幕密度磕道。
對于第一點(diǎn)供屉,在實(shí)際工作中是很難實(shí)現(xiàn)的。因為一般UI只會提供一套設(shè)計稿溺蕉,不會根據(jù)不同分辨率的屏幕來提供相應(yīng)的適配伶丐。但是我們沒辦法控制我們的App最終會運(yùn)行在什么分辨率的屏幕上,為了達(dá)到在不同屏幕上的顯示效果一直疯特,我們可以通過提供不同密度的位圖資源與Smallest Width方案來實(shí)現(xiàn)屏幕適配哗魂。
什么是Smallest Width適配
Smallest Width字面上的意思就是最小寬度,由可用屏幕區(qū)域的最小尺寸指定漓雅。 具體來說录别,設(shè)備的 smallestWidth 是屏幕可用高度和寬度的最小尺寸。
例如邻吞,如果布局要求屏幕區(qū)域的最小尺寸始終至少為 600 dp组题,則可使用此限定符創(chuàng)建布局資源 res/layout-sw600dp/
。僅當(dāng)可用屏幕的最小尺寸至少為 600dp 時抱冷,系統(tǒng)才會使用這些資源崔列,而不考慮 600dp 所代表的邊是用戶所認(rèn)為的高度還是寬度。smallestWidth 是設(shè)備的固定屏幕尺寸特性徘层;設(shè)備的 smallestWidth 不會隨屏幕方向的變化而改變峻呕。
所以我們可以根據(jù)需要適配的屏幕的sw值來提供不同的資源來實(shí)現(xiàn)屏幕適配。
UI設(shè)計與屏幕適配的一些基礎(chǔ)理念
我覺得很多屏幕適配教程都漏了一個很重要的點(diǎn)趣效,就是:沒有解釋清楚屏幕適配與UI設(shè)計之間的關(guān)系瘦癌!
一般在實(shí)際開發(fā)的時候,UI設(shè)計師都會提供一套UI稿與標(biāo)尺跷敬,工程師是通過這套標(biāo)尺來開發(fā)UI的讯私。UI如果我們要做好Android的屏幕適配,那么我們必須要明白的一點(diǎn)就是,UI稿在我們進(jìn)行界面開發(fā)中是充當(dāng)錨點(diǎn)的作用的斤寇。要適配其它的屏幕的話桶癣,必須要以這個基準(zhǔn)為基礎(chǔ)計算其它屏幕的dimens資源的值。
舉個例子:
例如娘锁,很多UI設(shè)計師都會以iPhone6的尺寸作為標(biāo)準(zhǔn)來制作設(shè)計稿與標(biāo)尺的牙寞,而iPhone6的屏幕寬度為375px,所以這個寬度為375px的設(shè)計稿就是我們屏幕適配的基準(zhǔn)了莫秆。
假設(shè)有一臺sw等于375dp的設(shè)備的話间雀,那么這個設(shè)備與設(shè)計稿對應(yīng)的關(guān)系就是1dp = 1px,那么我們就不需要進(jìn)行任何適配镊屎,直接把設(shè)計稿以px為單位的標(biāo)尺值以1:1的比例轉(zhuǎn)換成以dp為單位就可以了惹挟。
在這里,我們可以得出一個結(jié)論就是:屏幕適配需要以UI稿為基準(zhǔn)再制定合適的適配方案缝驳!
但是有一個問題就是连锯,每個UI設(shè)計師的喜好都是不一樣的,提供的設(shè)計稿的比例尺也不是固定的用狱。而且Android的屏幕碎片化非常嚴(yán)重运怖,我們需要適配的屏幕的sw的值也是變化多端的。所以如果每次都需要手動計算對應(yīng)的dimens值的話齿拂,非常耗時間與繁瑣驳规。網(wǎng)上提供了一些工具來快速生成對應(yīng)sw的dimens值,但是這些工具都會存在兩個缺點(diǎn):
沒辦法根據(jù)UI設(shè)計稿來轉(zhuǎn)換署海,所以不一定能100%還原設(shè)計稿效果
會生成大量無用的dimens值。其實(shí)如果我們細(xì)心觀察過設(shè)計稿的話医男,我們會發(fā)現(xiàn)砸狞,其實(shí)每份設(shè)計稿常用的px值都是固定的十來個。例如同樣以375px的設(shè)計稿為基準(zhǔn)的話镀梭,使用工具會生成1px ~ 375px對應(yīng)的dp值刀森,所以會存在大量的無用dimens值。這樣只會徒增安裝包的大小报账。
這個兩個缺點(diǎn)研底,可以使用筆者的calces.screen插件來解決,下文會介紹這個插件的使用方法與使用效果的透罢。
使用calces.screen快速實(shí)現(xiàn)Smalles Widths適配方案
適配前與適配后對比情況
還是以iPhone6的設(shè)計稿為例子榜晦,假如有下面這么一副設(shè)計稿,如果不進(jìn)行任何適配的話羽圃,在不同的設(shè)備上的顯示效果對比如下:
第一個手機(jī)就是上文中說到的sw = 375dp的手機(jī)乾胶,我們可以看到sw為其他值的手機(jī)上面,顯示效果都不如意。在sw = 411dp和sw = 900dp的設(shè)備上识窿,都留有大量的空白空間斩郎,而在sw = 360dp的設(shè)備上,則有超出屏幕范圍的現(xiàn)象喻频。我們適配的目標(biāo)就是:達(dá)到所有設(shè)備上顯示的效果都和設(shè)計稿(sw = 375dp上的效果)一致缩宜。
使用calces.screen插件適配后的效果如圖所示:
這里有一點(diǎn)需要注意的是,可以看到第三臺設(shè)備里面的適配還是有點(diǎn)問題甥温,大概留下了1dp左右的白邊脓恕。這個是pixel 2 XL的模擬器,可以看到窿侈,測量出來的sw值應(yīng)該是411dp的炼幔,但是經(jīng)過筆者的實(shí)際測量,發(fā)現(xiàn)sw應(yīng)該是412dp才對史简。有興趣的讀者可以自己在布局編輯器里面創(chuàng)建一個width為411dp的控件乃秀,可以看到在pixel 2 XL設(shè)備下也是有大概1dp的白邊的。所以這個1dp的誤差應(yīng)該是和設(shè)備有關(guān)的圆兵,這里貼上用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>
當(dāng)sw = 411dp 時,px_375的實(shí)際值時411dp殉农,所以這是符合我們的預(yù)期轉(zhuǎn)換結(jié)果的刀脏。
如何引入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/文件夾中創(chuàng)建dimens.xml文件,然后按照設(shè)計稿的標(biāo)尺把需要用到的尺寸寫到該文件下轮傍。例如:
<?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>
這就是我們的基準(zhǔn)dimens文件暂雹。
現(xiàn)在我們只需要把基準(zhǔn)尺寸與需要適配的尺寸通過Gradle配置就可以了,例如创夜,上面的例子中杭跪,我們需要適配的sw有:320dp, 411dp, 900dp,那么我們需要在modules的build.gradle文件下添加如下代碼:
screen{
dimens{
designPx 375
smallesWidths 320,375,411,900
scale BigDecimal.ROUND_UP
auto true
}
}
上面配置信息的對應(yīng)關(guān)系是:
designPx:設(shè)計稿的sw尺寸(單位px)
smallesWidths:需要適配的屏幕sw尺寸(單位dp)
scale: 數(shù)字取整的方式
因為Android系統(tǒng)只能適配整數(shù)單位的dp值驰吓,所以我們可以通過scale來配置具體的取正方式涧尿。這里直接取BigDecimal提供的round來實(shí)現(xiàn)。如果不設(shè)置的話檬贰,則會生成double類型的dp值(實(shí)際使用的時候會丟棄小數(shù)位)auto:是否自動生成dimens姑廉,當(dāng)auto為true時,每次build都會重新生成一次適配dimens文件偎蘸。
如果不設(shè)置auto或設(shè)置為false的話庄蹋,可以手動調(diào)用gradle任務(wù)來生成瞬内。
調(diào)用命令: /gradlew dimensCovert
也可以直接點(diǎn)擊gradle任務(wù)執(zhí)行,方式如下圖:
配置完畢后限书,重新build以下項目就可以看到生成的資源文件了虫蝶,如下圖:
為了不影響編譯時間auto建議設(shè)置為false,需要的時候再手動啟動任務(wù)生成適配資源文件倦西。
如何確定我們需要適配什么sw值能真?
除了自動生成sw外,我們還需要確定扰柠,我們的App需要支持那些sw值粉铐。最簡單的方法就是,先確定我們要支持哪些設(shè)備卤档。這里筆者給出一個建議就是蝙泼,市面上有非常多設(shè)備的sw值都是360dp的,所以我們必須要適配360dp的設(shè)備劝枣。至于其它的設(shè)備汤踏,我們可以這樣來確定,在開發(fā)者模式里面找到一項叫做最小寬度的參數(shù)舔腾,里面的值就是我們需要的sw值溪胶。具體如下圖:
例如,上面這個是Nexus S的sw值稳诚。如果我們不專門適配sw = 384dp的屏幕的話哗脖,那么系統(tǒng)就會默認(rèn)尋找低于384dp的適配資源(所以360dp是一個相對通用的適配值)。當(dāng)我們擁有測試設(shè)備的時候扳还,使用calces.screen適配是非常簡單的才避。那么如果我們不知道沒有測試設(shè)備呢?(例如有用戶反饋普办,某個設(shè)備下的適配有很大問題)
這里給大家推薦一個網(wǎng)站:Device Metrics
這個網(wǎng)站是Material Design的設(shè)備參數(shù)查找網(wǎng)站工扎,用戶在這里直接找到對應(yīng)設(shè)備的尺寸就可以了(之前的方法翻車了,溜了溜了)衔蹲。
一般情況下,sw為360dp和480dp的屏幕會比較常見呈础,所以我們必須要生成這兩套資源舆驶,如果需要支持Pad的話,則需要適配sw = 600dp 或 sw = 720dp的屏幕而钞,然后再根據(jù)實(shí)際情況適配其它sw值的屏幕沙廉。
到這里為止,我們就完成了Android基于sw方案的屏幕適配了臼节,非常簡單撬陵!
但是珊皿,本文還沒結(jié)束,這個插件除了提供自動實(shí)現(xiàn)基于sw方案的適配外巨税,還提供了一個殺手級功能:根據(jù)配置自動把生成對應(yīng)分辨率的位圖資源蟋定。當(dāng)我們需要適配多種不同屏幕密度的手機(jī)的時候,只需要提供一套高清位圖資源就可以了草添,解放你和UI設(shè)計師的雙手驶兜。
calces.screen實(shí)現(xiàn)位圖自動縮放適配
為不同密度的屏幕提供不同的位圖資源是Google官方推薦的屏幕適配做法。這樣做的好處是远寸,能使App在不同密度的屏幕上都能達(dá)到最好的效果抄淑,不會出現(xiàn)在高清屏下出現(xiàn)老年機(jī)的顯示效果,并且在不同密度的屏幕下都能保持相對穩(wěn)定的顯示效果驰后。下面是位圖資源密度對應(yīng)的比例關(guān)系:
密度限定符 | 比例關(guān)系 | 說明 |
---|---|---|
ldpi | 0.75 | 適用于低密度屏幕 (~120dpi) 的資源 |
mdpi | 1 | 適用于中密度屏幕 (~160dpi) 的資源(基線密度) |
hdpi | 1.5 | 適用于高密度屏幕 (~240dpi) 的資源 |
xhdpi | 2 | 適用于超高密度屏幕 (~320dpi) 的資源 |
xxhdpi | 3 | 適用于超超高密度屏幕 (~480dpi) 的資源 |
xxxhdpi | 4 | 適用于超超超高密度屏幕 (~640dpi) 的資源肆资。此限定符僅適用于 啟動器圖標(biāo)。 |
但是這里會產(chǎn)生一個問題灶芝,一般情況下郑原,位圖資源是UI設(shè)計師提供給我們的。我和很多UI設(shè)計師討論過监署,他們的方案就是先切一套最高清的圖片颤专,然后再根據(jù)需要進(jìn)行縮放,然后提供給工程師使用钠乏。
一般情況下栖秕,這種做法除了繁瑣點(diǎn)也沒什么問題。但是如果現(xiàn)在出現(xiàn)了一個情況晓避,就是需要支持更低密度的屏幕呢簇捍?這種情況只能讓UI設(shè)計師再縮放一套密度的位圖。那如果某部分位圖已經(jīng)不再使用了俏拱,需要刪除呢暑塑?那工程師需要把其它密度的位圖找出來再刪除。而且再往工程里面添加新的位圖的時候也需要手工添加锅必。
所以一般情況下事格,UI提供圖片資源 —— 工程師使用圖片資源這個過程中是純手工控制的。工作非常繁瑣并且沒什么意義搞隐,而且手動遷移的過程中還非常容易出錯(想想如果復(fù)制漏了某幾個密度的位圖資源會是什么畫面驹愚?)。所以calces.screen還提供了位圖管理功能劣纲。
calces.screen管理位圖
使用Screen的位圖縮放功能之前逢捺,先和設(shè)計師/產(chǎn)品商量好App最高需要支持哪個密度的屏幕寿烟。然后設(shè)計師以后只需要提供這套密度的位圖就可以了消略。之后我們只需要在modules的build.gradle中進(jìn)行配置,配置方式如下:
screen{
mipmap{
designDensity "xxxhdpi" //測試用,目前手機(jī)屏幕最高只支持到xxhdpi
mipmapDensity 'xxhdpi','xhdpi','hdpi','mdpi'
auto true
}
}
配置完之后漠其,重新build文件就可以了哈街,當(dāng)然不希望增加編譯時間的話夺饲,可以把a(bǔ)uto置為false或者不設(shè)置装盯。mipmap支持增量編譯功能,只會對文件夾中不存在的位圖進(jìn)行縮放恨憎,已存在則跳過蕊退,識別條件是文件名。手動啟動位圖縮放功能的方式和上述方式一致憔恳,任務(wù)名稱是mipmapZoom瓤荔。下面我們來看看轉(zhuǎn)換效果:
轉(zhuǎn)換前
轉(zhuǎn)換后
讀者可以點(diǎn)進(jìn)去查看一下轉(zhuǎn)換后的圖片尺寸,可以發(fā)現(xiàn)钥组,轉(zhuǎn)換后的圖片符合我們需要的的比例输硝。有興趣的讀者可以下載demo,把其它分辨率的位圖資源刪除程梦,通過mipmapZoom任務(wù)重新生成点把。
通過mipmapZoom任務(wù),可以大大減少UI設(shè)計師與工程師的工作量屿附,只需要管理一套位圖文件即可郎逃,把我們從機(jī)械化的任務(wù)中解放出來。
注:目前版本不支持位圖刪除功能挺份,所以當(dāng)我們需要刪除部分位圖的時候褒翰,需要把自動生成的圖片文件全部刪除,重新生成匀泊,后續(xù)版本會增加該功能优训。
結(jié)語
屏幕適配一直是移動端開發(fā)工程師的一大難題,面對琳瑯滿目的屏幕尺寸與屏幕密度各聘,我們一直在找一個更好的適配方案揣非。Smallest Width是目前Android中最簡單最好用的適配方案,沒有之一躲因,它是由系統(tǒng)提供支持的早敬,并且在適配時不會因為屏幕分辨率與設(shè)計稿的差異過大造成一些奇奇怪怪的問題(大屏幕上面變糊,小屏幕又顯得像素過于密集)大脉。筆者這個適配方案是基于Smallest Width與提供多套位圖為基礎(chǔ)搁嗓,通過Gradle插件來自動處理sw比例計算與文件生成、位圖自動縮放來實(shí)現(xiàn)一個相對更好的適配方案箱靴。
calces.screen開發(fā)的初衷時簡化UI設(shè)計師與Android工程師的工作量,目前已經(jīng)基本達(dá)城這一目標(biāo)荷愕。
好了衡怀,關(guān)于calces.screen插件的介紹就到此為止了棍矛,這里再一次提醒大家,如果覺得calces對你有所幫助的話抛杨,可以點(diǎn)下star够委,鼓勵下作者。如果有一些更好的想法的話怖现,可以參與這一開源項目茁帽。筆者會一直維護(hù)這個項目的,致力于減輕Android工程師的負(fù)擔(dān)屈嗤,把重復(fù)的機(jī)械性工作全部交給Gradle來處理潘拨。