字節跳動屏幕適配方案解讀

說來慚愧,這個方案在微信公眾號推出來的時候,我才去了解我司自己的適配方案。字節跳動屏幕適配方案

重點

  1. 為什么要做屏幕適配
  2. 從數據上告訴你安卓手機屏幕的碎片化
  3. 我司的適配方案
  4. 適配中出現的問題
  5. 實際演練

為什么要做屏幕適配

在Android開發中,由于Android的碎片化嚴重,屏幕分辨率千奇百怪,想要在各種分辨率的設備上顯示一致的效果,適配成本越來越高。雖然Android官方提供了dp單位來適配,但是在各種奇怪分辨率的手機下表現的不近人意。

Android手機屏幕的碎片化

在大部分場景下我們是不需要關心屏幕適配,因為很多ui不會有太多元素,要不然就是RecyclerView來展示數據,但是也有不少的場景UI會比較復雜,我們需要關心手機適配,接下來,從Google和友盟的數據上看下當前手機屏幕的大小和分辨率

Google數據

image

可以看到Normal占比是最多的,Normal就是寬度為480dp的手機。而在Normal手機中dpi為320的手機又居多。

small -> 320dp
Normal -> 480dp
Large -> 600dp
XLarge -> 720dp

友盟數據

image

image
名次 屏幕分辨率 活躍占比 趨勢
1 1920x1080 (16:9) 28.9
2 1280x720 (16:9) 22.5
3 1080x1821 (3:5) 3.9
4 960x540 (16:9) 3.3
5 854x480 (16:9) 2.8
6 720x1208 (9:16) 2.6
7 1184x720 (5:3) 2.4
8 1776x1080 (5:3) 2.1
9 2560x1440 (16:9) 2
10 2016x1080 (17:9) 1.9
11 1794x1080 (5:3) 1.8
12 2040x1080 (17:9) 1.3
13 2160x1080 (18:9) 1.3

從上面的數據可以看到安卓的手機市場,各種比例、分辨率、尺寸的手機層出不窮,如果我們按照Google官方的適配方案,那么我們基本上可以不用做其他需求了,ui改動一個像素點,你可能需要改動十幾個layout的布局文件。

字節跳動屏幕適配方案

這節會從基礎到新的適配方案,詳細看下同事是怎樣解決這個世紀難題。

傳統dp適配方式

android中的dp在渲染前會將dp轉換為px,計算公式為

px = density * dp 


density = dpi/160 


px = dp * (dpi/160)

dpi(pot per inch)就是每英寸的多少個點,dpi是根據屏幕真實的分辨率和尺寸計算的,每個設配可能都不一樣。

[圖片上傳失敗...(image-436913-1547873896006)]

拿MIX2做個例子,MIX2的尺寸是5.99英寸,分辨率是2160*1080象素。根據上面的公式可以算出MIX2的dpi為402。

這里有個小問題
我們通過代碼

val mix2dpi= resources.displayMetrics.densityDpi
println("mix2 dpi is : $mix2dpi")

//打印出 440

為什么和我們計算出來的不一致,難道公式是假的?那肯定不是的,小米手機雖然在手機信息上寫的5.99英寸但是這是整個手機屏幕,而不是真正的顯示區域,MIX2的真正顯示區域是5.5英寸,這樣是不是就對應起來了。

這樣問題就來了

假設我們的設計小哥哥給的UI設計圖是按照屏幕375dp設計的,那么在上述的設備上,屏幕的寬度是1080/(440/160)=392.7dp,也就是要比設計圖要款。這種情況下,即時使用dp,也無法達到和設計圖相同的效果。同事還存在部分設備寬度不足375dp,那么就會出現超出屏幕的情況出現。

除此之外還有很多設備并沒有按照上述的公式來實現,因此dpi的值是非常亂的。用dp進行適配也是差強人意。

字節跳動適配方案

先了解幾個知識點

  • dp和px的轉換公式為:px = dp * density
  • dp轉換的場景都是通過DisplayMetrics來進行計算的
  • DisplayMetrics#density 就是上述的density
  • DisplayMetrics#densityDpi 就是上述的dpi
  • DisPlayMetrics#scaledDensity 字體的縮放銀子,正常情況下和density相等,但是調節系統字體大小后會改變這個值

那么應用中所有dp的計算都是更DisplayMetrics這個類有關,所以只用針對這個類進行操作就好了。

我們可以將DisPlayMetrics理解為三個部分

  1. System,初始分配,包含第三方庫。
  2. Application,整個應用的DisplayMetrics
  3. Activity,單個Activity頁面的DisplayMetrics
        //系統的屏幕尺寸
        val systemDM = Resources.getSystem().displayMetrics
        //app整體的屏幕尺寸
        val appDM = activity.application.resources.displayMetrics
        //activity的屏幕尺寸
        val activityDM = activity.resources.displayMetrics

我們這里以寬度為375dp的寬度為基準去適配,這個值你可以改為任何值。

        // 適配屏幕的寬度
        activityDM.density = activityDM.widthPixels / tartgetDP.toFloat()
        // 適配相應比例的字體大小
        activityDM.scaledDensity = activityDM.density * (systemDM.scaledDensity / systemDM.density)
        // 適配dpi
        activityDM.densityDpi = (160 * activityDM.density).toInt()

看下整個適配的工具類,特別簡單

object ScreenUtil {

    fun adapterScreen(activity: Activity, targetDP: Int, isVertical: Boolean) {
        //系統的屏幕尺寸
        val systemDM = Resources.getSystem().displayMetrics
        //app整體的屏幕尺寸
        val appDM = activity.application.resources.displayMetrics
        //activity的屏幕尺寸
        val activityDM = activity.resources.displayMetrics

        if (isVertical) {
            // 適配屏幕的高度
            activityDM.density = activityDM.heightPixels / targetDP.toFloat()
        } else {
            // 適配屏幕的寬度
            activityDM.density = activityDM.widthPixels / targetDP.toFloat()
        }
        // 適配相應比例的字體大小
        activityDM.scaledDensity = activityDM.density * (systemDM.scaledDensity / systemDM.density)
        // 適配dpi
        activityDM.densityDpi = (160 * activityDM.density).toInt()
    }

    fun resetScreen(activity: Activity) {
        //系統的屏幕尺寸
        val systemDM = Resources.getSystem().displayMetrics
        //app整體的屏幕尺寸
        val appDM = activity.application.resources.displayMetrics
        //activity的屏幕尺寸
        val activityDM = activity.resources.displayMetrics

        activityDM.density = systemDM.density
        activityDM.scaledDensity = systemDM.scaledDensity
        activityDM.densityDpi = systemDM.densityDpi

        appDM.density = systemDM.density
        appDM.scaledDensity = systemDM.scaledDensity
        appDM.densityDpi = systemDM.densityDpi
    }
}

使用中出現的問題

使用時需要注意以下幾點:

  1. 盡量只在當前頁面生效,包括activity,fragment,dialog,view,需要在setCOntentView()或者inflate之前調用這個方法,在結束onDestroy,onDismiss,onDetachWindow()的時候調用resetScreen()方法。
  2. 在頁面中需要彈出toastdialog的時候需要調用resetScreen,不然toastdialog的頁面大小和字體大小會被影響。
  3. 記住一點使用前調用adapterScreen,時候后調用resetScreen

實際操作

工欲善其事必先利其器

我們要使用這種方式,首先要把Android Studio的xml預覽界面調節成設計師給的設計圖的屏幕尺寸。這里已經向抖音的設計師確認,給我們的所有設計圖都是按照750*1334象素 -> 375*667dp的規格設計。也就是說density=2,對應dpi=160*2=320,對應的手機屏幕大小為4.8英寸

在預覽界面怎么創建相應的界面呢?

tools -> AVD Manager -> Create Virtual Device... -> New Hardware Profile -> screen sieze: 4.8 Resolution: 750 x 1334

什么?還不會,那我只能把所有的圖都貼出來了。


image

image

image

現在準備完成了,可以開始開發了

比對效果

未使用前,為了適配小屏手機,在大屏手機上看就會很丑

beforeAdapter.png

使用后,兩個頁面基本相同了

afterAdapter.png
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容

  • 目錄介紹 1.屏幕適配定義 2.相關重要的概念2.1 屏幕尺寸[物理尺寸]2.2 屏幕分辨率[px]2.3 屏幕像...
    楊充211閱讀 1,559評論 0 1
  • 推薦Android兩種屏幕適配方案 前言 在Android開發中,由于Android碎片化嚴重,屏幕分辨率千奇百怪...
    Gxinyu閱讀 12,904評論 3 53
  • 本文記錄一些適配問題的研究,基礎概念不做過多介紹。 Android在做屏幕適配的時候一般考慮兩個因素:分辨率和dp...
    developerzjy閱讀 7,377評論 1 24
  • 前言:之前很火的屏幕適配方案不知道大家都去嘗試過寫進項目中沒,應該有一部分人在隔岸觀火,大概的原因就是目前并沒有遇...
    _那個人閱讀 4,460評論 5 34
  • 我是日記星球239號星寶寶,來自深圳的葉子。我相信日積月累的力量,積跬步以致千里!這是第175篇日記。 深圳中午迎...
    水晶媽咪閱讀 254評論 0 0