美文网首页前端文章收集
字节跳动屏幕适配方案解读

字节跳动屏幕适配方案解读

作者: lycknight | 来源:发表于2019-01-19 13:01 被阅读0次

    说来惭愧,这个方案在微信公众号推出来的时候,我才去了解我司自己的适配方案。字节跳动屏幕适配方案

    重点

    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

    相关文章

      网友评论

        本文标题:字节跳动屏幕适配方案解读

        本文链接:https://www.haomeiwen.com/subject/daoadqtx.html