浅谈适配都有那些
- 宽高限定符适配
- 鸿洋的 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 这种单位的模拟设备创建方法
如果您在 layout 文件中使用 mm 作为单位进行布局 (需要通过 AutoSizeConfig.getInstance().getUnitsManager().setSupportSubunits(Subunits.MM); 打开对单位 mm 的支持),则可以根据公式 (sqrt(纵向分辨率2+横向分辨率2))/25.4 求出屏幕尺寸,然后创建模拟设备 (只用填写屏幕尺寸和分辨率)
我们的设计图是以苹果设计为主的 也就是750*1334
选择当前副单位的设备预览