Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Android ByteDance Screen Adaptation #40

Open
yunshuipiao opened this issue May 25, 2019 · 0 comments
Open

Android ByteDance Screen Adaptation #40

yunshuipiao opened this issue May 25, 2019 · 0 comments
Labels

Comments

@yunshuipiao
Copy link
Owner

Android ByteDance Screen adaptation

[TOC]

这篇文章讲解 Android 不同屏幕的适配方式

传统的 dp 适配方式的缺点

android 中的各种各种单位渲染之前都会转为 px。

  • px = density * dp
  • density = dpi / 160
  • px = dp * (dpi / 160)

上面的 dpi 根据屏幕真实的分辨率和尺寸来计算,每个设备都不一样。

屏幕尺寸,分辨路和像素密度

一般情况下,手机的分辨率是宽* 高,屏幕大小以 寸 为单位,关系如下:dpi * 屏幕密度 = 宽^2 + 高^2

比如:手机屏幕分辨率为 1920 * 1080, 屏幕尺寸为5寸的话, dpi = 440.

存在的问题

如上,加入设计图以 360dp 来设计。上面的屏幕实际为 1080/(440/160)= 392.7dp。

这种情况下, 即使用 dp 也无法做到在不同屏幕下可以显示同样的效果。同时还存在部分屏幕不足 360dp,显示不全的情况。

新的适配方式

一般我们设计图都是以固定的尺寸来设计的。

比如以分辨率 1920px * 1080px 来设计,以 density 为 3 来标注,也就是屏幕其实是 640dp * 360dp。如果我们想在所有设备上显示完全一致,其实是不现实的,因为屏幕高宽比不是固定的,16:9、4:3甚至其他宽高比层出不穷,宽高比不同,显示完全一致就不可能了。但是通常下,我们只需要以宽或高一个维度去适配,比如我们Feed是上下滑动的,只需要保证在所有设备中宽的维度上显示一致即可,再比如一个不支持上下滑动的页面,那么需要保证在高这个维度上都显示一致,尤其不能存在某些设备上显示不全的情况。同时考虑到现在基本都是以 dp 为单位去做的适配,如果新的方案不支持 dp,那么迁移成本也非常高。

因此,需求两点如下:

  1. 支持以宽或者高一个维度去适配,保持该维度和设计图一致
  2. 支持 dp 和 sp 单位,控制迁移成本到最小

从dp和px的转换公式 :px = dp * density

可以看出,如果设计图宽为360dp,想要保证在所有设备计算得出的px值都正好是屏幕宽度的话,我们只能修改 density 的值。

先来熟悉下 DisplayMetrics 中和适配相关的几个变量:

  • DisplayMetrics#density 就是上述的density
  • DisplayMetrics#densityDpi 就是上述的dpi
  • DisplayMetrics#scaledDensity 字体的缩放因子,正常情况下和density相等,但是调节系统字体大小后会改变这个值

最终方案

下面假设设计图宽度是360dp,以宽维度来适配。

那么适配后的 density = 设备真实宽(单位px) / 360,接下来只需要把我们计算好的 density 在系统中修改下即可,代码实现如下:

同时在 Activity.onCreate 方法中调用下。代码比较简单,也没有涉及到系统非公开api的调用,因此理论上不会影响app稳定性。

如果用户修改了字体大小,这里字体并不会随着变化, 因为还需要修改 scaledDensity。

最终代码如下:

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
    }
}
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant