Android系統發佈十多年以來,關於Android的UI的適配一直是開發環節中最重要的問題,可是我看到仍是有不少小夥伴對Android適配方案不瞭解。恰好,近期準備對糗事百科Android客戶端設計一套UI尺寸適配方案,能夠和小夥伴們詳細的聊一聊這個問題。android
Android適配最核心的問題有兩個,其一,就是適配的效率,即把設計圖轉化爲App界面的過程是否高效,其二如何保證明現UI界面在不一樣尺寸和分辨率的手機中UI的一致性。這兩個問題都很重要,一個是保證咱們開發的高效,一個是保證咱們適配的成效;今天咱們就這兩個核心的問題來聊一聊Android的適配方案。git
首先,你們都知道,在標識尺寸的時候,Android並不推薦咱們使用px這個真實像素單位,由於不一樣的手機之間,分辨率是不一樣的,好比一個96*96像素的控件在分辨率愈來愈高的手機上會在總體UI中看起來愈來愈小。github
出現相似於上圖這樣這樣,總體的佈局效果可能會變形,因此px這個單位在佈局文件中是不推薦的。框架
針對這種狀況,Android推薦使用dp做爲尺寸單位來適配UI.佈局
那麼什麼是dp?dp指的是設備獨立像素,以dp爲尺寸單位的控件,在不一樣分辨率和尺寸的手機上表明瞭不一樣的真實像素,好比在分辨率較低的手機中,可能1dp=1px,而在分辨率較高的手機中,可能1dp=2px,這樣的話,一個96*96dp的控件,在不一樣的手機中就能表現出差很少的大小了。那麼這個dp是如何計算的呢? 咱們都知道一個公式: px = dp(dpi/160) 系統都是經過這個來判斷px和dp的數學關係,spa
那麼這裏又出現了一個問題,dpi是什麼呢?設計
dpi是像素密度,指的是在系統軟件上指定的單位尺寸的像素數量,它每每是寫在系統出廠配置文件的一個固定值。code
我爲何要強調它是軟件系統上的概念?由於你們買手機的時候,每每會聽到另外一個叫ppi的參數,這個在手機屏幕中指的也是像素密度,可是這個是物理上的概念,它是客觀存在的不會改變。dpi是軟件參考了物理像素密度後,人爲指定的一個值,這樣保證了某一個區間內的物理像素密度在軟件上都使用同一個值。這樣會有利於咱們的UI適配。繼承
好比,幾部相同分辨率不一樣尺寸的手機的ppi可能分別是是430,440,450,那麼在Android系統中,可能dpi會所有指定爲480.這樣的話,dpi/160就會是一個相對固定的數值,這樣就能保證相同分辨率下不一樣尺寸的手機表現一致。資源
而在不一樣分辨率下,dpi將會不一樣,好比:
... | 1080*720 | 1920*1080 |
---|---|---|
dpi | 320 | 480 |
dpi/160 | 2 | 3 |
根據上面的表格,咱們能夠發現,720P,和1080P的手機,dpi是不一樣的,這也就意味着,不一樣的分辨率中,1dp對應不一樣數量的px(720P中,1dp=2px,1080P中1dp=3px),這就實現了,當咱們使用dp來定義一個控件大小的時候,他在不一樣的手機裏表現出相應大小的像素值。
咱們能夠說,經過dp加上自適應佈局和weight比例佈局能夠基本解決不一樣手機上適配的問題,這基本是最原始的Android適配方案。
這種方式存在兩個小問題,第一,這隻能保證咱們寫出來的界面適配絕大部分手機,部分手機仍然須要單獨適配,爲何dp只解決了90%的適配問題,由於並非全部的1080P的手機dpi都是480,好比Google 的Pixel2(1920*1080)的dpi是420,也就是說,在Pixel2中,1dp=2.625px,這樣會致使相同分辨率的手機中,這樣,一個100dp*100dp的控件,在通常的1080P手機上,可能都是300px,而Pixel 2 中 ,就只有262.5px,這樣控件的實際大小會有所不一樣。
爲了更形象的展現,假設咱們在佈局文件中把一個ImageView的寬度設置爲360dp,那麼在下面兩張圖中表現是不同的:
圖一是1080P,480dpi的手機,圖二是1080P,420dpi的手機
從上面的佈局中能夠看到,一樣是1080P的手機,差別是比較明顯的。在這種狀況下,咱們的UI可能須要作一些微調甚至單獨適配。
第二個問題,這種方式沒法快速高效的把設計師的設計稿實現到佈局代碼中,經過dp直接適配,咱們只能讓UI基本適配不一樣的手機,可是在設計圖和UI代碼之間的鴻溝,dp是沒法解決的,由於dp不是真實像素。並且,設計稿的寬高每每和Android的手機真實寬高差異極大,以咱們的設計稿爲例,設計稿的寬高是375px*750px,而真實手機可能廣泛是1080*1920,
那麼在平常開發中咱們是怎麼跨過這個鴻溝的呢?基本都是經過百分比啊,或者經過估算,或者設定一個規範值等等。總之,當咱們拿到設計稿的時候,設計稿的ImageView是128px*128px,當咱們在編寫layout文件的時候,卻不能直接寫成128dp*128dp。在把設計稿向UI代碼轉換的過程當中,咱們須要耗費至關的精力去轉換尺寸,這會極大的下降咱們的生產力,拉低開發效率。
爲了高效的實現UI開發,出現了新的適配方案,我把它稱做寬高限定符適配。簡單說,就是窮舉市面上全部的Android手機的寬高像素值:
設定一個基準的分辨率,其餘分辨率都根據這個基準分辨率來計算,在不一樣的尺寸文件夾內部,根據該尺寸編寫對應的dimens文件。
好比以480x320爲基準分辨率
那麼對於800*480的分辨率的dimens文件來講,
x1=(480/320)*1=1.5px
x2=(480/320)*2=3px
...
這個時候,若是咱們的UI設計界面使用的就是基準分辨率,那麼咱們就能夠按照設計稿上的尺寸填寫相對應的dimens引用了,而當APP運行在不一樣分辨率的手機中時,這些系統會根據這些dimens引用去該分辨率的文件夾下面尋找對應的值。這樣基本解決了咱們的適配問題,並且極大的提高了咱們UI開發的效率,
可是這個方案有一個致命的缺陷,那就是須要精準命中才能適配,好比1920x1080的手機就必定要找到1920x1080的限定符,不然就只能用統一的默認的dimens文件了。而使用默認的尺寸的話,UI就極可能變形,簡單說,就是容錯機制不好。
不過這個方案有一些團隊用過,咱們能夠認爲它是一個比較成熟有效的方案了。
鴻洋大佬的適配方案的項目也來自於寬高限定符方案的啓發。
使用方法也很簡單:
第一步: 在你的項目的AndroidManifest中註明你的設計稿的尺寸。
<meta-data android:name="design_width" android:value="768"> </meta-data> <meta-data android:name="design_height" android:value="1280"> </meta-data>
第二步: 讓你的Activity繼承自AutoLayoutActivity.
而後咱們就能夠直接在佈局文件裏面使用具體的像素值了,好比,設計稿上是96*96,那麼咱們能夠直接寫96px,APP運行時,框架會幫助咱們根據不一樣手機的具體尺寸按比例伸縮。
這能夠說是一個極好的方案,由於它在寬高限定符適配的基礎上更進一步,而且解決了容錯機制的問題,能夠說完美的達成了開發高效和適配精準的兩個要求。
可是咱們可以想到,由於框架要在運行時會在onMeasure裏面作變換,咱們自定義的控件可能會被影響或限制,可能有些特定的控件,須要單獨適配,這裏面可能存在的暗坑是不可預見的,還有一個比較重要的問題,那就是整個適配工做是有框架完成的,而不是系統完成的,一旦使用這個框架,將來一旦遇到很難解決的問題,替換起來是很是麻煩的,並且項目一旦中止維護,後續的升級就只能靠你本身了,這種代價團隊可否承受?固然,它已經中止維護了。
不過僅僅就技術方案而言,不能否認,這是一個很好的開源項目。
討論的上述幾種適配方案都是能夠實際用於開發中的比較成熟的方案,並且確實有不少開發者正在使用。不過因爲他們各自都存在一些缺陷,因此咱們使用了上述方案後還須要花費額外的精力着手解決這些可能存在的缺陷。
那麼,是否存在一種相對比較完美,沒有明顯的缺陷的方案呢?
smallestWidth適配,或者叫sw限定符適配。指的是Android會識別屏幕可用高度和寬度的最小尺寸的dp值(其實就是手機的寬度值),而後根據識別到的結果去資源文件中尋找對應限定符的文件夾下的資源文件。
這種機制和上文提到的寬高限定符適配原理上是同樣的,都是系統經過特定的規則來選擇對應的文件。
舉個例子,小米5的dpi是480,橫向像素是1080px,根據px=dp(dpi/160),橫向的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-sw<N>dp文件夾下的數值是不一樣的,好比values-sw360dp和values-sw400dp,
當系統識別到手機的smallestWidth值時,就會自動去尋找和目標數據最近的資源文件的尺寸。
其次,從穩定性上,它也優於上述方案。原生的dp適配可能會碰到Pixel 2這種有些特別的手機須要單獨適配,可是在smallestWidth適配中,經過計算Pixel 2手機的的smallestWidth的值是411,咱們只須要生成一個values-sw411dp(或者取整生成values-sw410dp也沒問題)就能解決問題。
smallestWidth的適配機制由系統保證,咱們只須要針對這套規則生成對應的資源文件便可,不會出現什麼難以解決的問題,也根本不會影響咱們的業務邏輯代碼,並且只要咱們生成的資源文件分佈合理,,即便對應的smallestWidth值沒有找到徹底對應的資源文件,它也能向下兼容,尋找最接近的資源文件。
固然,smallestWidth適配方案有一個小問題,那就是它是在Android 3.2 之後引入的,Google的本意是用它來適配平板的佈局文件(可是實際上顯然用於diemns適配的效果更好),不過目前全部的項目應該最低支持版本應該都是4.0了(糗事百科這麼老的項目最低都是4.0哦),因此,這問題其實也不重要了。
生成diemns文件的過程以及數據計算方法上面已經講清楚了,你們徹底能夠本身去生成這些文件,我在這裏附贈生成values-sw的項目代碼,你們直接拿去用,是Java工程。點擊這裏獲取項目地址