騷年你的屏幕適配方式該升級了!-今日頭條適配方案

原文地址: www.jianshu.com/p/55e0fca23…android

如下是 騷年你的屏幕適配方式該升級了! 系列文章,歡迎轉發以及分享:git

掃描或點擊如下二維碼,加入技術交流 QQ 羣 455850365程序員

前言

這個月在 Android 技術圈中 屏幕適配 這個詞曝光率挺高的,爲何這麼說呢?由於這個月陸續有多個大佬發佈了屏幕適配相關的文章,公佈了本身承認的屏幕適配方案github

上上個星期 Blankj 老師發表了一篇力挺今日頭條屏幕適配方案的 文章,提出了不少優化的方案,並開源了相關源碼bash

上個星期 拉丁吳 老師在 鴻神 的公衆號上發佈了一篇 文章,詳細描述了市面上主流的幾種屏幕適配方案,併發布了他的 smallestWidth 限定符適配方案和相關源碼 (其實早就發佈了),文章寫的很好,建議你們去看看併發

其實你們最關注的不是市面上有多少種屏幕適配方案,而是本身的項目該選擇哪一種屏幕適配方案,能夠看出兩位老師最終選擇的屏幕適配方案都是不一樣的app

我下面就來分析分析,我做爲一個才接觸這兩個屏幕適配方案的吃瓜羣衆,我是怎麼來驗證這兩種屏幕適配方案是否可行,以及怎樣根據它們的優缺點來選擇一個最適合本身項目的屏幕適配方案框架

這是我推薦給你們的屏幕適配框架,原本想放到最後做爲福利的,懼怕你們看不到,因此就將連接放到這裏,提早送給你們佈局

Github : 您的 Star 是我堅持的動力 ✊post

淺談適配方案

拉丁吳 老師的文章中談到了兩個比較經典的屏幕適配方案,在我印象中十分深入,我想大多數兄弟都用過,在個人開發生涯裏也是有很長一段時間都在用這兩種屏幕適配方案

第一種就是寬高限定符適配,什麼是寬高限定符適配呢

├── src/main
│   ├── res
│   ├── ├──values
│   ├── ├──values-800x480
│   ├── ├──values-860x540
│   ├── ├──values-1024x600
│   ├── ├──values-1024x768
│   ├── ├──...
│   ├── ├──values-2560x1440
複製代碼

就是這種,在資源文件下生成不一樣分辨率的資源文件,而後在佈局文件中引用對應的 dimens,你們必定還有印象

第二種就是 鴻神AndroidAutoLayout

這兩種方案都已經逐漸退出了歷史的舞臺,爲何想必你們都知道,不知道的建議看看 拉丁吳 老師的文章,因此這兩種方案我在文章中就不在闡述了,主要講講如今最主流的兩種屏幕適配方案,今日頭條適配方案smallestWidth 限定符適配方案

建議你們不清楚這兩個方案的先看看這兩篇文章,才清楚我在講什麼,後面我要講解它們的原理,以及驗證這兩種方案是否真的可行,最後對他們進行深刻對比,對於他們的一些缺點給予對應的解決方案,絕對乾貨

今日頭條屏幕適配方案

原理

上面已經告知,不瞭解這兩個方案的先看看上面的兩篇文章,因此這裏我就假設你們已經看了上面的文章或者以前就瞭解過這兩個方案,因此在本文中我就再也不闡述 DPIDensity 以及一些比較基礎的知識點,上面的文章已經闡述的夠清楚了

今日頭條屏幕適配方案的核心原理在於,根據如下公式算出 density

當前設備屏幕總寬度(單位爲像素)/ 設計圖總寬度(單位爲 dp) = density

density 的意思就是 1 dp 佔當前設備多少像素

爲何要算出 density,這和屏幕適配有什麼關係呢?

public static float applyDimension(int unit, float value,
                                       DisplayMetrics metrics)
    {
        switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }
複製代碼

你們都知道,無論你在佈局文件中填寫的是什麼單位,最後都會被轉化爲 px,系統就是經過上面的方法,將你在項目中任何地方填寫的單位都轉換爲 px

因此咱們經常使用的 pxdp 的公式 dp = px / density,就是根據上面的方法得來的,density 在公式的運算中扮演着相當重要的一步

要看懂下面的內容,還得明白,今日頭條的適配方式,今日頭條適配方案默認項目中只能以高或寬中的一個做爲基準,進行適配,爲何不像 AndroidAutoLayout 同樣,高以高爲基準,寬以寬爲基準,同時進行適配呢

這就引出了一個如今比較棘手的問題,大部分市面上的 Android 設備的屏幕高寬比都不一致,特別是如今大量全面屏的問世,這個問題更加嚴重,不一樣廠商推出的全面屏手機的屏幕高寬比均可能不一致

這時咱們只以高或寬其中的一個做爲基準進行適配,就會有效的避免佈局在高寬比不一致的屏幕上出現變形的問題

明白這個後,我再來講說 densitydensity 在每一個設備上都是固定的,DPI / 160 = density屏幕的總 px 寬度 / density = 屏幕的總 dp 寬度

  • 設備 1,屏幕寬度爲 1080px480DPI,屏幕總 dp 寬度爲 1080 / (480 / 160) = 360dp

  • 設備 2,屏幕寬度爲 1440560DPI,屏幕總 dp 寬度爲 1440 / (560 / 160) = 411dp

能夠看到屏幕的總 dp 寬度在不一樣的設備上是會變化的,可是咱們在佈局中填寫的 dp 值倒是固定不變的

這會致使什麼呢?假設咱們佈局中有一個 View 的寬度爲 100dp,在設備 1 中 該 View 的寬度佔整個屏幕寬度的 27.8% (100 / 360 = 0.278)

但在設備 2 中該 View 的寬度就只能佔整個屏幕寬度的 24.3% (100 / 411 = 0.243),能夠看到這個 View 在像素越高的屏幕上,dp 值雖然沒變,可是與屏幕的實際比例卻發生了較大的變化,因此肉眼的觀看效果,會愈來愈小,這就致使了傳統的填寫 dp 的屏幕適配方式產生了較大的偏差

這時咱們要想完美適配,那就必須保證這個 View 在任何分辨率的屏幕上,與屏幕的比例都是相同的

這時咱們該怎麼作呢?改變每一個 Viewdp 值?不現實,在每一個設備上都要經過代碼動態計算 Viewdp 值,工做量太大

若是每一個 Viewdp 值是固定不變的,那咱們只要保證每一個設備的屏幕總 dp 寬度不變,就能保證每一個 View 在全部分辨率的屏幕上與屏幕的比例都保持不變,從而完成等比例適配,而且這個屏幕總 dp 寬度若是還能保證和設計圖的寬度一致的話,那咱們在佈局時就能夠直接按照設計圖上的尺寸填寫 dp

屏幕的總 px 寬度 / density = 屏幕的總 dp 寬度

在這個公式中咱們要保證 屏幕的總 dp 寬度設計圖總寬度 一致,而且在全部分辨率的屏幕上都保持不變,咱們須要怎麼作呢?屏幕的總 px 寬度 每一個設備都不一致,這個值是確定會變化的,這時今日頭條的公式就派上用場了

當前設備屏幕總寬度(單位爲像素)/ 設計圖總寬度(單位爲 dp) = density

這個公式就是把上面公式中的 屏幕的總 dp 寬度 換成 設計圖總寬度,原理都是同樣的,只要 density 根據不一樣的設備進行實時計算並做出改變,就能保證 設計圖總寬度 不變,也就完成了適配

驗證方案可行性

上面已經把原理分析的很清楚了,不少文章只是一筆帶過這個公式,公式雖然很簡單但咱們仍是想曉得這是怎麼來的,因此我就反向推理了一遍,若是仍是看不懂,那我只能說我盡力了,原理講完了,那咱們再來現場驗證一下這個方案是否可行?

假設設計圖總寬度爲 375 dp,一個 View 在這個設計圖上的尺寸是 50dp * 50dp,這個 View 的寬度佔整個設計圖寬度的 13.3% (50 / 375 = 0.133),那咱們就來驗證下在使用今日頭條屏幕適配方案的狀況下,這個 View 與屏幕寬度的比例在分辨率不一樣的設備上是否還能保持和設計圖中的比例一致

驗證設備 1

屏幕總寬度爲 1080 px,根據今日頭條的的公式求出 density1080 / 375 = 2.88 (density)

這個 50dp * 50dpView,系統最後會將高寬都換算成 px50dp * 2.88 = 144 px (根據公式 dp * density = px)

144 / 1080 = 0.133View 實際寬度與 屏幕總寬度 的比例和 View 在設計圖中的比例一致 (50 / 375 = 0.133),因此完成了等比例縮放

某些設備總寬度爲 1080 px,可是 DPI 可能不一樣,是否會對今日頭條適配方案產生影響?其實這個方案根本沒有根據 DPI 求出 density,是根據本身的公式求出的 density,因此這對今日頭條的方案沒有影響

上面只能肯定在全部屏幕總寬度爲 1080 px 的設備上能完成等比例適配,那咱們再來試試其餘分辨率的設備

驗證設備 2

屏幕總寬度爲 1440 px,根據今日頭條的的公式求出 density1440 / 375 = 3.84 (density)

這個 50dp * 50dpView,系統最後會將高寬都換算成 px50dp * 3.84 = 192 px (根據公式 dp * density = px)

192 / 1440 = 0.133View 實際寬度與 屏幕總寬度 的比例和 View 在設計圖中的比例一致 (50 / 375 = 0.133),因此也完成了等比例縮放

兩個不一樣分辨率的設備都完成了等比例縮放,證實今日頭條屏幕適配方案在不一樣分辨率的設備上都是有效的,若是你們還心存疑慮,能夠再試試其餘分辨率的設備,其實到最後得出的比例不會有任何誤差, 都是 0.133

優勢

  1. 使用成本很是低,操做很是簡單,使用該方案後在頁面佈局時不須要額外的代碼和操做,這點能夠說完虐其餘屏幕適配方案

  2. 侵入性很是低,該方案和項目徹底解耦,在項目佈局時不會依賴哪怕一行該方案的代碼,並且使用的仍是 Android 官方的 API,意味着當你遇到什麼問題沒法解決,想切換爲其餘屏幕適配方案時,基本不須要更改以前的代碼,整個切換過程幾乎在瞬間完成,會少不少麻煩,節約不少時間,試錯成本接近於 0

  3. 可適配三方庫的控件和系統的控件(不止是是 ActivityFragmentDialogToast 等全部系統控件均可以適配),因爲修改的 density 在整個項目中是全局的,因此只要一次修改,項目中的全部地方都會受益

  4. 不會有任何性能的損耗

缺點

暫時沒發現其餘什麼很明顯的缺點,已知的缺點有一個,那就是第三個優勢,它既是這個方案的優勢也一樣是缺點,可是就這一個缺點也是很是致命的

只須要修改一次 density,項目中的全部地方都會自動適配,這個看似解放了雙手,減小了不少操做,可是實際上反應了一個缺點,那就是隻能一刀切的將整個項目進行適配,但適配範圍是不可控的

這樣不是很好嗎?這樣原本是很好的,可是應用到這個方案是就很差了,由於我上面的原理也分析了,這個方案依賴於設計圖尺寸,可是項目中的系統控件、三方庫控件、等非咱們項目自身設計的控件,它們的設計圖尺寸並不會和咱們項目自身的設計圖尺寸同樣

當這個適配方案不分類型,將全部控件都強行使用咱們項目自身的設計圖尺寸進行適配時,這時就會出現問題,當某個系統控件或三方庫控件的設計圖尺寸和和咱們項目自身的設計圖尺寸差距很是大時,這個問題就越嚴重

舉個栗子

假設一個三方庫的 View,做者在設計時,把它設計爲 100dp * 100dp,設計圖的最大寬度爲 1000dp,這個 View 在設計圖中的比例是 100 / 1000 = 0.1,意思是這個 View 的寬度在設計圖中佔整個寬度的 10%,若是咱們要完成等比例適配,那這個三方庫 View 在全部的設備上與屏幕的總寬度的比例,都必須保持在 10%

這時在一個使用今日頭條屏幕適配方案的項目上,設置的設計圖最大寬度若是是 1000dp,那這個三方庫 View,與項目自身均可以完美的適配,但當咱們項目自身的設計圖最大寬度不是 1000dp,是 500dp 時,100 / 500 = 0.2,能夠看到,比例發生了較大的變化,從 10% 上升爲 20%,明顯這個三方庫 View 高於做者的預期,比以前更大了

這就是兩個設計圖尺寸不一致致使的很是嚴重的問題,當兩個設計圖尺寸差距越大,那適配的效果也就天差萬別了

解決方案

方案 1

調整設計圖尺寸,由於三方庫多是遠程依賴的,沒法修改源碼,也就沒法讓三方庫來適應咱們項目的設計圖尺寸,因此只有咱們自身做出修改,去適應三方庫的設計圖尺寸,咱們將項目自身的設計圖尺寸修改成這個三方庫的設計圖尺寸,就能完成項目自身和三方庫的適配

這時項目的設計圖尺寸修改了,因此項目佈局文件中的 dp 值,也應該按照修改的設計圖尺寸,按比例增減,保持與以前設計圖中的比例不變

可是若是爲了適配一個三方庫修改整個項目的設計圖尺寸,是很是不值得的,因此這個方案支持以 Activity 爲單位修改設計圖尺寸,至關於每一個 Activity 均可以自定義設計圖尺寸,由於有些 Activity 不會使用三方庫 View,也就不須要自定義尺寸,因此每一個 Activity 都有控制權的話,這也是最靈活的

但這也有個問題,當一個 Activity 使用了多個設計圖尺寸不同的三方庫 View,就會一樣出現上面的問題,這也就只有把設計圖改成與幾個三方庫比較折中的尺寸,才能勉強緩解這個問題

方案 2

第二個方案是最簡單的,也是按 Activity 爲單位,取消當前 Activity 的適配效果,改用其餘的適配方案

使用中的問題

有些文章中提到了今日頭條屏幕適配方案能夠將設計圖尺寸填寫成以 px 爲單位的寬度和高度,這樣咱們在佈局文件中,也就能直接填寫設計圖上標註的 px 值,省掉了將 px 換算爲 dp 的時間 (大部分公司的設計圖都只標註 px 值),並且照樣能完美適配

可是我建議你們千萬不要這樣作,仍是老老實實的以 dp 爲單位填寫 dp 值,爲何呢?

直接填寫 px 雖然剛開始佈局的時候很爽,可是這個坑就已經埋上了,會讓你後面很爽,有哪些坑?

第一個坑

這樣無疑於使項目強耦合於這個方案,當你遇到沒法解決的問題想切換爲其餘屏幕適配方案的時候,layout 文件裏曾經填寫的 px 值都會做爲 dp

好比你的設計圖實際寬度爲 1080px,你不換算爲 360dp (1080 / 3 = 360),卻直接將 1080px 做爲這個方案的設計圖尺寸,那你在 layout 文件中,填寫的也都是設計圖上標註的 px 值,可是單位倒是 dp

一個在設計圖上 300px * 300pxView,你能夠直接在 layout 文件中填寫爲 300dp,而因爲這個方案能夠動態改變 density 的緣由仍是能夠作到等比例適配,很是爽!

但你不要忘了,這樣你就強耦合於這個方案了,由於當你不使用這個方案時,density 是不可變的!

舉個栗子

使用這個方案時,在屏幕寬度爲 1080px 的設備上,將設計圖寬度直接填寫爲 1080,根據今日頭條公式

當前設備屏幕總寬度 / 設計圖總寬度 = density

這時得出 density 爲 1 (1080 / 1080 = 1),因此你在 layout 文件中你填寫的 300dp 最後轉換爲 px 也是 300px (300dp * 1 = 300px 根據公式 dp * density = px)

在這個方案的幫助下很是完美,和設計圖如出一轍完成了適配

但當你不使用這個方案時,density 的換算公式就變爲官方的 DPI / 160 = density, 在這個屏幕寬度爲 1080px480dpi 的設備上,density 就固定爲 3 (480 / 160 = 3)

這時再來看看你以前在 layout 文件中填寫的 dp,換算成 px900 px (300dp * 3 = 900px 根據公式 dp * density = px)

本來在在設計圖上爲 300pxView,這時卻達到了驚人的 900px,3倍的差距,恭喜你,你已經強耦合於這個方案了,你要不全部 layout 文件都改一遍,要不繼續使用這個方案

第二個坑

第二個坑其實就是剛剛在上面說的今日頭條適配方案的缺點,當某個系統控件或三方庫控件的設計圖尺寸和和咱們項目自身的設計圖尺寸差距很是大時,這個問題就越嚴重

你若是直接填寫以 px 爲設計圖的尺寸,這不用想,確定和全部的三方庫以及系統控件的設計圖尺寸都不同,並且差距都很是之大,至少兩三倍的差距,這時你在當前頁面彈個 Toast 就能夠明顯看到,比以前小不少,能夠說是天差萬別,用其餘三方庫 View,也是同樣的,會小不少

由於你以 px 爲單位填寫設計圖尺寸,人家卻用的 dp,差距能不大嗎,你若是老老實實用 dp,哪怕三方庫的設計圖尺寸和你項目自身的設計圖尺寸不同,那也差距不大,小到必定程度,基本都不用調整,能夠忽略不計,並且不少三方庫的設計圖尺寸其實也都是那幾個大衆尺寸,很大可能和你項目自身的設計圖尺寸同樣

總結

能夠看到我講的很是詳細,能夠說比今日頭條官方以及任何博客寫的都清楚,從原理到優缺點再到解決方案包羅萬象,由於篇幅有限,若是我還想把 smallestWidth 限定符適配方案寫的這麼詳細,那估計這篇文章得有一萬字了

因此我把此次的屏幕適配文章歸位一個系列,一共分爲三篇,第一篇詳細的講 今日頭條屏幕適配方案,第二篇詳細的講 smallestWidth 限定符適配方案,第三篇詳細講兩個方案的深刻對比以及如何選擇,併發布我根據 今日頭條屏幕適配方案 優化的屏幕適配框架 AndroidAutoSize

今日頭條屏幕適配方案 官方公佈的核心源碼只有 30 行不到,但我這個框架的源碼有 1500 行以上,在保留原有特性的狀況下增長了很多功能和特性,功能增長了很多,可是使用上卻變簡單了

<manifest>
    <application>            
        <meta-data android:name="design_width_in_dp" android:value="360"/>
        <meta-data android:name="design_height_in_dp" android:value="640"/>           
     </application>           
</manifest>
複製代碼

只要這一步填寫了設計圖的高寬以 dp 爲單位,你什麼都不作,框架就開始適配了

你們能夠提早看看我是怎麼封裝和優化的,我後面的第三篇文章會給出這個框架的原理分析,敬請期待


關於你們的評論以及關注的問題,我在這裏統一回復一下:

感謝,你們的關注和回覆,我介紹這個今日頭條的屏幕適配方案並非說他有多麼完美,只是他確實有效並且能幫咱們減小不少開發成本

對於不少人說的 DPI 的存在,不就是爲了讓大屏能顯示更多的內容,若是一個大屏手機和小屏手機,顯示的內容都相同,那用戶買大屏手機又有什麼意義呢,我以爲你們對 DPI 的理解是對的,這個觀點我也是認同的,Google 設計 DPI 時可能也是這麼想的,可是有一點你們沒考慮到,Android 的碎片化太嚴重了

爲何 Android 誕生這麼多年,Android 的百分比庫層出不窮,按理說他們都違背了上面說的這個理念,但爲何還有這麼多人去研究百分比庫經過各類方式去實現百分比佈局 (谷歌官方也曾出過百分比庫)?爲何?很簡單,由於需求啊!爲何需求,由於開發成本低啊!爲何今日頭條的這個屏幕適配方案如今能這麼火,由於他的開發成本是目前全部屏幕適配方案中最低的啊!

DPI 的意義誰又不懂呢?難道就你懂,今日頭條這種大公司的程序員不懂這個道理嗎?今日頭條這麼大的公司難道不想把每一個機型每一個版本的設備都適配完美,讓本身的 App 體驗更好,哪怕付出更大的成本,有些時候想象是美好的,可是這個投入的成本,誰又能承擔呢,連今日頭條這麼大的公司,這麼雄厚的資本都沒選擇投入更大的成本,對每一個機型進行更精細化的適配,難道市面上的中小型公司又有這個能力投入這麼大的成本嗎?

魚和熊掌不可兼得,DPI 的意義在 Google 的設計理念中是徹底正確的,但不是全部公司都能承受這個成本,想必今日頭條的程序員,也是由於今日頭條 App 的用戶量足夠多,機型分佈足夠廣,也是被屏幕適配這個問題折磨的不要不要的,纔想出這麼個不這麼完美可是卻頗有效的方案

公衆號

掃碼關注個人公衆號 JessYan,一塊兒學習進步,若是框架有更新,我也會在公衆號上第一時間通知你們


如下是 騷年你的屏幕適配方式該升級了! 系列文章,歡迎轉發以及分享:


Hello 我叫 JessYan,若是您喜歡個人文章,能夠在如下平臺關注我

-- The end

相關文章
相關標籤/搜索