谈一谈今日头条屏幕适配方案实现"增量"适配

浅谈适配都有那些

  • 宽高限定符适配
  • 鸿洋的 AndroidAutoLayout
  • 今日头条的适配方案

(一)什么是宽高限定符适配

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

这种,在资源文件下生成不同分辨率的资源文件,然后在布局文件中引用对应的 dimens

缺点:对应不同分辨率有着不同的资源文件的弊端,相比也清楚,每出一款不同分辨率的机型都需要创建一个单独的资源,其实是很难涵盖所有机型的

(二) 鸿洋的 AndroidAutoLayout

AndroidAutoLayout地址
这是一款百分比屏幕适配框架,按道理这是一款真正的适配屏幕的的框架
但是,这框架却不适用于现在了,因为这款百分比布局的框架是建立在16 :9的屏幕的不变的前提下
而当下新时代全面屏横行,AndroidAutoLayout已经水土不服了,在不是16 :9的屏幕下View会严重变形

所以这种百分比的适配方案已经要退出历史舞台

(三)今日头条的适配方案

要说今日头条的适配就要说一下dp,Android推荐使用dp作为尺寸单位来适配UI
android中的dp在渲染前会将dp转为px,计算公式:

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

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

(1)dp 直接适配

  • 那么什么是dp?

dp指的是设备独立像素,以dp为尺寸单位的控件,在不同分辨率和尺寸的手机上代表了不同的真实像素,比如在分辨率较低的手机中,可能1dp=1px,而在分辨率较高的手机中,可能1dp=2px,这样的话,一个96*96dp的控件,在不同的手机中就能表现出差不多的大小了。

  • 那么这个dp是如何计算的呢?

我们都知道一个公式:从dp和px的转换公式 :px = dp * density 系统都是通过这个来判断px和dp的数学关系,

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

通过阅读源码,我们可以得知,density 是 DisplayMetrics 中的成员变量,而 DisplayMetrics 实例通过 Resources#getDisplayMetrics 可以获得,而Resouces通过Activity或者Application的Context获得。

例:

getResources().getDisplayMetrics().density * dp

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

  • DisplayMetrics#density 就是上述的density

  • DisplayMetrics#densityDpi 就是上述的dpi

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

  • 今日头条的适配方案

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

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

 class ScreenAdaptUtil {
     companion object {
         fun setCustomDensity(activity:Activity,application: Application){
             val applicationMetrics = application.resources.displayMetrics
              //真是屏幕宽度/设计图宽(360dp)换算成当前设备的targetDensity
             var targetDensity:Float = (applicationMetrics.widthPixels/360).toFloat()
             var targetDensityDpi = 160*targetDensity

            //因为dp的转换公式px = density * dp,我们根据屏幕修改了density也就达到了我们适配的目的
             applicationMetrics.density = targetDensity
            //正常情情况下字体scaledDensity是和density是一致的,但是会因为修改字体而修改scaledDensity,为了保持适配我们使用targetDensity避免这个问题
             applicationMetrics.scaledDensity = targetDensity
             applicationMetrics.densityDpi = targetDensityDpi.toInt()

             val activityMetrics = activity.resources.displayMetrics
             activityMetrics.density = targetDensity
             activityMetrics.scaledDensity = targetDensity
             activityMetrics.densityDpi = targetDensityDpi.toInt()
         }
     }
}

在activity中应用

 ScreenAdaptUtil.setCustomDensity(this,application)

这是一个简单的今日头条适配方案的的简单代码

(三) 基于今日头条的适配方案的AndroidAutoSize

AndroidAutoSize地址
具体使用参考AndroidAutoSize使用说明,这里不做特别的说明

AndroidAutoSize或者说今日头条真的没有问题吗?

其实还是在实践中发现了问题

因为是修改了是常量,所以会 导致APP全局都会生效所以会引起:

  • 今日头条适配方案会影响一些三方库和系统控件
  • 老项目的某些页面之前使用了 dp 进行布局,并且 AndroidAutoSize 可能这些页面会有一些不良影响

(三) 使用AndroidAutoSize副单位做增量适配

  • 在Android开发中,dp、sp两个主单位,pt、in、mm三个冷门副单位,AndroidAutoSize支持副单位,如果使用副单位,可以规避系统控件或三方库控件使用的不良影响
  • 如果使用副单位,则可以直接填写像素尺寸,不需要再将像素转化为 dp

使用副单位的预览布局问题

布局时的实时预览在开发阶段是一个很重要的环节,很多情况下 Android Studio 提供的默认预览设备并不能完全展示我们的设计图,所以我们就需要自己创建模拟设备,我们这里以mm 这种单位的模拟设备创建方法


创建预览设备.png

如果您在 layout 文件中使用 mm 作为单位进行布局 (需要通过 AutoSizeConfig.getInstance().getUnitsManager().setSupportSubunits(Subunits.MM); 打开对单位 mm 的支持),则可以根据公式 (sqrt(纵向分辨率2+横向分辨率2))/25.4 求出屏幕尺寸,然后创建模拟设备 (只用填写屏幕尺寸和分辨率)

我们的设计图是以苹果设计为主的 也就是750*1334


创建预览设备.png

选择当前副单位的设备预览


创建预览设备.png

(每天学习一点点.每天进步一点点,分享不宜路过点个赞呀,喜欢的点个关注后续更新不断)

你可能感兴趣的:(谈一谈今日头条屏幕适配方案实现"增量"适配)