听说你还在用dp做屏幕适配?
大家在Android開發時,肯定會覺得屏幕適配是個尤其痛苦的事,各種屏幕尺寸適配起來巨煩無比。如果我們換個角度我們看下這個問題,不知道大家有沒有了解過web前端開發,或者說大家對于網頁都不陌生吧,其實適配的問題在web頁面的設計中理論上也存在,為什么這么說呢?電腦的顯示器的分辨率、包括手機分辨率,我敢說分辨率的種類遠超過Android設備的分辨率,那么有一個很奇怪的現象:
為什么Web頁面設計人員從來沒有說過,屏幕適配好麻煩?
那么,到底是什么原因,讓網頁的設計可以在千差萬別的分辨率的分辨率中依舊能給用戶一個優質的體驗呢?帶著這個疑惑,我問了下一個前端朋友,朋友睜大眼睛問我:適配是什么?? 前端似乎看來的確沒有這類問題,后來在我仔細的追問后,她告訴我,噢 這個尺寸呀,我們一般都加個viewport,我都是設置為20%縮放的~~ 追根到底,其實就是一個原因,網頁提供了縮放比計算大小。
同樣的,大家拿到UI給的設計圖以后,是不是抱怨過UI妹妹標識的都是px,而我項目里面用dp,這都什么玩意😂,和UI解釋她也不理解,開發同樣也是一臉無奈。所以能不能有一套完美的解決方案來解決Android工程師和UI妹妹間的矛盾,實現UI給出一個固定尺寸的設計稿,然后你在編寫布局的時候不用思考,無腦照抄上面標識的像素值,就能達到完美適配。理想夠豐滿,但現實夠殘酷:
由于Android系統的開放性,任何用戶、開發者、OEM廠商、運營商都可以對Android進行定制,于是導致:
-
Android系統碎片化:小米定制的MIUI、魅族定制的flyme、華為定制的EMUI等等,當然其都是基于Google原生系統定制的
-
Android機型屏幕尺寸碎片化:5寸、5.5寸、6寸等等
-
Android屏幕分辨率碎片化:320x480、480x800、720x1280、1080x1920
據友盟指數顯示,統計至2015年12月,支持Android的設備共有27796種
當Android系統、屏幕尺寸、屏幕密度出現碎片化的時候,就很容易出現同一元素在不同手機上顯示不同的問題。
試想一下這么一個場景:
為4.3寸屏幕準備的UI設計圖,運行在5.0寸的屏幕上,很可能在右側和下側存在大量的空白;而5.0寸的UI設計圖運行到4.3寸的設備上,很可能顯示不下。
屏幕種類這么多,那么就需要一套完美的方案去解決適配問題,介紹屏幕適配方案之前,先簡單介紹下Android屏幕中用到的一些相關重要概念::**
屏幕尺寸
· 含義:手機對角線的物理尺寸
· 單位:英寸(inch),1英寸=2.54cm
Android手機常見的尺寸有5寸、5.5寸、6寸等等
屏幕分辨率
· 含義:手機在橫向、縱向上的像素點數總和
一般描述成屏幕的"寬x高”=AxB
含義:屏幕在橫向方向(寬度)上有A個像素點,在縱向方向(高)有B個像素點
例子:1080x1920,即寬度方向上有1080個像素點,在高度方向上有1920個像素點
- 單位:px(pixel),1px=1個像素點
UI設計師的設計圖會以px作為統一的計量單位
- Android手機常見的分辨率:320x480、480x800、720x1280、1080x1920、 1080x2340
屏幕像素密度
-
含義:每英寸的像素點數
-
單位:dpi(dots per ich)
假設設備內每英寸有160個像素,那么該設備的屏幕像素密度=160dpi
- 安卓手機對于每類手機屏幕大小都有一個相應的屏幕像素密度:
| 低密度(ldpi) | 240x320 | 120 |
| 中密度(mdpi) | 320x480 | 160 |
| 高密度(hdpi) | 480x800 | 240 |
| 超高密度(xhdpi) | 720x1280 | 320 |
| 超超高密度(xxhdpi) | 1080x1920 | 480 |
屏幕尺寸、分辨率、像素密度三者關系
一部手機的分辨率是寬*高,屏幕大小是以寸為單位,那么三者的關系是:
不懂沒關系,在這里舉個例子
假設一部手機的分辨率是1080x1920(px),屏幕大小是5寸,問密度是多少?
解:請直接套公式
密度無關像素
- 含義:density-independent pixel,叫dp或dip,與終端上的實際物理像素點無關。
- 單位:dp,可以保證在不同屏幕像素密度的設備上顯示相同的效果
- dp與px的轉換
因為ui設計師給你的設計圖是以px為單位的,Android開發則是使用dp作為單位的,那么我們需要進行轉換:
| 低密度(ldpi) | 240x320 | 120 | 1dp=0.75px |
| 中密度(mdpi) | 320x480 | 160 | 1dp=1px |
| 高密度(hdpi) | 480x800 | 240 | 1dp=1.5px |
| 超高密度(xhdpi) | 720x1280 | 320 | 1dp=2px |
| 超超高密度(xxhdpi) | 1080x1920 | 480 | 1dp=3px |
在Android中,規定以160dpi(即屏幕分辨率為320x480)為基準:1dp=1px
獨立比例像素
- 含義:scale-independent pixel,叫sp或sip
- 單位:sp
Android開發時用此單位設置文字大小,可根據字體大小首選項進行縮放。
推薦使用12sp、14sp、18sp、22sp作為字體設置的大小,不推薦使用奇數和小數,容易造成精度的丟失問題;小于12sp的字體會太小導致用戶看不清
請把上面的概念記住,因為下面講解都會用到!
適配方案比較
1. dp原生方案
2. dimen基于px和dp的適配(寬高限定符和smallestWidth適配)
3. 頭條屏幕適配方案
4. 頭條適配方案改進版本
dp原生方案
前言:統一以px為單位有什么問題?
Android屏幕適配由來已久,關鍵在于屏幕尺寸與屏幕分辨率的變化巨大,而很多UI工程師為了兼容iOS和Android的適配,這樣導致設計出來的UI稿是以px單位標注的。在成千上百種機型面前,px單位已難以適應。
1.同樣尺寸,不同分辨率:
1080px的寬度上顯示100px 比例是100/1080
720px的寬度上顯示100px 比例是100/720
2.同分辨率,不同尺寸:
1080px在4.7寸上顯示100px
1080px在6.1寸上顯示100px
如果使用多套px文件方案來適配,市面上少說上百種寸,需要的文件太多了。
不同分辨率的屏幕該如何適配
這時候就需要用到dp方案來解決了,所以dp究竟解決了什么問題?
以下公式表示了,同樣尺寸上不同分辨率(不同density)的設備,每1dp所代表的像素數量是不一樣的。
480 dpi上 1dp = 1 * 3 = 3px
320 dpi上 1dp = 1 * 2 = 2px
240 dpi上 1dp = 1 * 1.5 = 1.5px
160 dpi上 1dp = 1 * 1 = 1px
120 dpi上 1dp = 1 * 0.75 = 0.75px
但是所表示的物理長度(160dp=1in)是一樣的。
160 dp在density=3上表示480px,物理長度為1 in
160 dp在density=2上表示320px,物理長度為1 in
160 dp在density=1.5上表示240px,物理長度為1 in
160 dp在density=1上表示160px,物理長度為1 in
160 dp在density=0.75上表示120px,物理長度為1 in
由上可知,dp單位的使用就意味著你在這些同樣尺寸但是不同分辨率的設備上看到的大小一樣,此時各設備上顯示的比例也就一致了。
dp方案沒有解決什么問題
舉個例子:
屏幕分辨率為:1920*1080,屏幕尺寸為5吋的話,那么dpi為440。假設我們UI設計圖是按屏幕寬度為360dp來設計的,那這樣會存在什么問題呢?
在上述設備上,屏幕寬度其實為1080/(440/160)=392.7dp,也就是屏幕是比設計圖要寬的。這種情況下, 即使使用dp也是無法在不同設備上顯示為同樣效果的。 同時還存在部分設備屏幕寬度不足360dp,這時就會導致按360dp寬度來開發實際顯示不全的情況。
而且上述屏幕尺寸、分辨率和像素密度的關系,很多設備并沒有按此規則來實現, 因此dpi的值非常亂,沒有規律可循,從而導致使用dp適配效果差強人意。
dimen基于px和dp的適配(寬高限定符和smallestWidth適配)
dimen基于dp適配 SmallestWidth限定符
原理:
這種適配依據的是最小寬度限定符。指的是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文件夾下面的資源文件。這個特性就完美的解決了上文提到的寬高限定符的容錯問題。
缺點:
- 侵入性強
- Android 私人訂制的原因,寬度方面參差不齊,不可能適配所有的手機。
- 項目中增加了N個文件夾,上拉下拉查看文件非常不方便:想看string或者color資源文件需要拉很多再能到達。
- 通過寬度限定符就近查找的原理,可以看出來匹配出來的大小不夠準確。
- 是在Android 3.2 以后引入的,Google的本意是用它來適配平板的布局文件(但是實際上顯然用于diemns適配的效果更好),不過目前SPX所有的項目應該最低支持版本應該都是5.1了,所以這問題其實也不重要了。
dimens基于px的適配 寬高限定符適配
原理:
根據市面上手機分辨率的占比分析,我們選定一個占比例值大的(比如1280*720)設定為一個基準,然后其他分辨率根據這個基準做適配。
基準的意思(比如320*480的分辨率為基準)是:
寬為320,將任何分辨率的寬度分為320份,取值為x1到x320
長為480,將任何分辨率的高度分為480份,取值為y1到y480
例如對于800 * 480的分辨率設備來講,需要在項目中values-800x480目錄下的dimens.xml文件中的如下設置(當然了,可以通過工具自動生成):
<resources> <dimen name="x1">1.5px</dimen> <dimen name="x2">3.0px</dimen> <dimen name="x3">4.5px</dimen> <dimen name="x4">6.0px</dimen> <dimen name="x5">7.5px</dimen></pre>可以看到x1 = 480 / 基準 = 480 / 320 = 1.5 ;它的意思就是同樣的1px,在320/480分辨率的手機上是1px,在480/800的分辨率的手機上就是1*1.5px,px會根據我們指定的不同values文件夾自動適配為合適的大小。
驗證方案:
簡單通過計算驗證下這種方案是否能達到適配的效果,例如設計圖上有一個寬187dp的View。
分辨率為480 * 800
- 設計圖占寬比: 187dp / 375dp = 0.498
- 實際在480 800占寬比 = 187 1.28px / 480 = 0.498
分辨率為1080 * 1920
- 設計圖占寬比: 187dp / 375dp = 0.498
- 實際在1080 1920占寬比 = 187 2.88px / 1080 = 0.498
- 計算高同理
缺點:
- 侵入性強
- 需要精準命中資源文件才能適配,比如1920x1080的手機就一定要找到1920x1080的限定符,否則就只能用統一的默認的dimens文件了。而使用默認的尺寸的話,UI就很可能變形,簡單說,就是容錯機制很差。
- Android不同分辨率的手機實在太多了,可能你說主流就可以,的確小公司主流就可以,淘寶這種App肯定不能只適配主流手機??丶谠O計圖上顯示的大小以及控件之間的間隙在小分辨率和大分辨率手機上天壤之別,你會發現大屏幕手機上控件超級大??赡苣銜X得正常,畢竟分辨率不同。但實際效果大的有些夸張。
- 占據資源大:好幾百KB,甚至多達1M或跟多。
頭條屏幕適配方案
梳理需求:
首先來梳理下我們的需求,一般我們設計圖都是以固定的尺寸來設計的。比如以分辨率1920px * 1080px來設計,以density為3來標注,也就是屏幕其實是640dp * 360dp。如果我們想在所有設備上顯示完全一致,其實是不現實的,因為屏幕高寬比不是固定的,16:9、4:3甚至其他寬高比層出不窮,寬高比不同,顯示完全一致就不可能了。但是通常下,我們只需要以寬或高一個維度去適配,比如我們Feed是上下滑動的,只需要保證在所有設備中寬的維度上顯示一致即可,再比如一個不支持上下滑動的頁面,那么需要保證在高這個維度上都顯示一致,尤其不能存在某些設備上顯示不全的情況。同時考慮到現在基本都是以dp為單位去做的適配,如果新的方案不支持dp,那么遷移成本也非常高。
因此,總結下大致需求如下:
- 支持以寬或者高一個維度去適配,保持該維度上和設計圖一致;
- 支持dp和sp單位,控制遷移成本到最小。
找方案兼容突破口
從dp和px的轉換公式 :
px=dp?density\color{red}{px = dp * density}px=dp?density
可以看出,如果設計圖寬為360dp,想要保證在所有設備計算得出的px值都正好是屏幕寬度的話,我們只能修改 density 的值。通過閱讀源碼,我們可以得知,density 是 DisplayMetrics 中的成員變量,而 DisplayMetrics 實例通過 Resources#getDisplayMetrics 可以獲得,而Resouces通過Activity或者Application的Context獲得。
先來熟悉下 DisplayMetrics 中和適配相關的幾個變量:
- DisplayMetrics#density 就是上述的density
- DisplayMetrics#densityDpi 就是上述的dpi
- DisplayMetrics#scaledDensity 字體的縮放因子,正常情況下和density相等,但是調節系統字體大小后會改變這個值
是不是Android中所有的dp和px的換算都是通過 DisplayMetrics 中相關的值來計算的呢?
- 首先來看看布局文件中的dp轉化,最終都是調用TypedValue#applyDimension來進行住轉化
- 圖片的decode,BitmapFactory#decodeResourceStream方法:
當然還有些其他dp轉換的場景,基本都是通過 DisplayMetrics 來計算的,這里不再詳述。因此,想要滿足上述需求,我們只需要修改DisplayMetrics 中和 dp 轉換相關的變量即可。
最終方案:
下面假設設計圖寬度是360dp,以寬維度來適配。
那么適配后 自定義density = 設備真實寬(單位px) / 360,接下來只需要把我們計算好的 density 在系統中修改下即可,代碼實現如下:
同時在 Activity#onCreate 方法中調用下。代碼比較簡單,也沒有涉及到系統非公開api的調用,因此理論上不會影響app穩定性。
缺點:
- 只能支持以高或寬中的一個作為基準進行適配。
- 只需要修改一次 density,項目中的所有地方都會自動適配,這個看似解放了雙手,減少了很多操作,但是實際上反應了一個缺點,那就是只能一刀切的將整個項目進行適配,但適配范圍是不可控的。項目中如果采用了系統控件、三方庫控件、等不是我們項目自身設計的控件,這時就會出現和我們項目自身的設計圖尺寸差距非常大的問題。
頭條適配方案改進版本
大致思路:在頭條適配方案的基礎上,通過重寫Activity的getResources(),重寫冷門單位pt作為基準單位,它是Android 中的一個長度單位:表示一個點,是屏幕的物理尺寸,其大小為 1 英寸的 1 / 72,也就是 72pt 等于 1 英寸
- AdaptScreenUtils
-
使用方法
以寬度320為基準進行適配@Override
public Resources getResources() {
return AdaptScreenUtils.adaptWidth(super.getResources(),320);
}
假設我現在需要在屏幕中心有個按鈕,寬度和高度為我們屏幕寬度的1/2,我可以怎么編寫布局文件呢?
<FrameLayout> <Buttonandroid:layout_gravity="center"android:gravity="center"android:text="@string/hello_world"android:layout_width="160pt"android:layout_height="160pt"/> </FrameLayout>效果
480 x 800 - mdpi(160dpi)
720 x 1280 - xhdpi(320dpi)
1080 x 1920 - xxhdpi(480dpi)
可以看到效果圖中 WebView 對之后的 View 并沒有產生適配失效的問題,這是之前適配所不能解決的問題。
優點
1. 無侵入性
用了這個之后依然可以使用dp包括其他任何單位,對從前使用的布局不會造成任何影響,在老項目中開發新功能你可以膽大地加入該適配方案,新項目的話更可以毫不猶豫地采用該適配,并且在關閉該關閉后,pt 效果等同于 dp 哦。
2. 靈活性高
如果你想要對某個 View 做到不同分辨率的設備下,使其尺寸在適配維度上所占比例一致的話,那么對它使用 pt 單位即可,如果你不想要這樣的效果,而是想要更大尺寸的設備顯示更多的內容,那么可以像從前那樣寫 dp、sp 什么的即可,結合這兩點,在界面布局上你就可以游刃有余地做到你想要的效果。
3. 不會影響系統 View 和三方 View 的大小
這點其實在無侵入性中已經表現出來了,由于頭條的方案是直接修改 DisplayMetrics#density 的 dp 適配,這樣會導致系統 View 尺寸和原先不一致,比如 Dialog、Toast、 尺寸,同樣,三方 View 的大小也會和原先效果不一致,這也就是選擇 pt 適配的原因之一。
4. 不會失效
因為不論頭條的適配還是其他三方庫適配,都會存在 DisplayMetrics#density 被還原的情況,需要自己重新設置回去,最顯著的就是界面中存在 WebView 的話,由于其初始化的時候會還原 DisplayMetrics#density 的值導致適配失效,當然這點已經有解決方案了,但還會有很多其他情況會還原 DisplayMetrics#density 的值導致適配失效。而我這方案就是為了解決這個痛點,不讓 DisplayMetrics 中的值被還原導致適配失效。
缺點:
只能適配寬或者高其中一邊,但這也是絕大部分適配方案的痛點所在,長和寬只能適配其一,好在大部分公司在采用這些方案去適配是都采用優先適配寬,然后在長上面以滑動形式去進行解決;
小結:
雖然 dimen基于px和dp的適配這種方案能涵蓋市面上所有機型屏幕的適配,但是冗余的dimen文件會讓工程師們生不如死,而且這種方案侵入性非常強,一旦使用將使得回退變得非常的困難;頭條適配方案和頭條適配優化方案作為一種侵入性不是很強的方式進行接入,能完美解決代碼冗余問題,而且總體方案靈活性很高,但只能選擇寬或者高作為唯一維度去進行適配;
上述方案都能用來解決屏幕適配問題,每種方案都有其獨特的優缺點,因此最終選取哪種方案因人而異
參考文章:
Android屏幕適配和方案【整理】
Android 屏幕適配:最全面的解決方案
Android 屏幕適配方案
一種極低成本的Android屏幕適配方式
Android聽說你還在用dp單位做屏幕適配?
?? 謝謝支持
以上便是本次分享的全部內容,希望對你有所幫助_
喜歡的話別忘了 分享、點贊、收藏 三連哦~。
歡迎關注公眾號 程序員巴士,一輛有趣、有范兒、有溫度的程序員巴士,涉獵大廠面經、程序員生活、實戰教程、技術前沿等內容,關注我,交個朋友。
總結
以上是生活随笔為你收集整理的听说你还在用dp做屏幕适配?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python人口统计_python数据分
- 下一篇: 关于远程连接access数据库问题