Android 提供了 dp 单位来做适配,那我们为什么还要进行屏幕适配呢?我们先来看一张图
举个例子,如果一个屏幕的分辨率是 1080 x 1920,屏幕大小是 5,那密度是多少呢?
px 和 dp 的换算公式为:px = dp * ( dpi / 160 )
使用 dp 单位进行布局设计可以帮助实现跨设备的屏幕适配。但是,在以下情况下,使用 dp 单位可能会导致布局适配不理想:
- 超大屏幕或超小屏幕设备:在超大屏幕设备上,使用 dp 单位可能导致布局显得过于稀疏,而在超小屏幕设备上,可能会导致布局过于拥挤。
- 特殊屏幕比例和形状:一些设备可能具有非标准的屏幕比例和形状,例如圆形屏幕,这种情况下使用 dp 单位可能无法完全适配。
- 多窗口模式:在支持多窗口模式的设备上,同一应用可能会被同时展示在不同大小的窗口中,这时候需要考虑更灵活的布局适配方案。
SmallestWidth 适配
这是依据最小宽度限定符,指的是 Android 会识别屏幕宽度最小尺寸的 dp 值,然后根据识别到的结果去寻找对应限定符的文件夹下的资源文件,这个可以使用 Android Studio 里的 SmallestWidth Dimens 插件来完成。需要注意的是,如果没有 values-sw360dp 文件夹,系统会向下寻找,比如离 360dp 最近的只有 values-sw340dp,那么系统就会选择 values-sw340dp 文件夹下的资源文件。
安装完成后可以在 Tools 看到 SmallestWidth,点击它会出现如下界面
点击 Generate 会自动生成这些
基于 px 的宽高限定符适配
这种方式特别适合特定分辨率的屏幕适配,比方说,这些 Android 设备的屏幕分辨率种类不多,那就比较合适了。如果针对手机屏幕的话,那显然不适合,毕竟不同分辨率的手机实在太多了。而且,需要注意的是,这种方式,需要精准命中资源文件才能适配,比如 1920x1080 的屏幕就一定要找到 1920x1080 的限定符,如果没有专门适配分辨率为 1920x1080 的资源文件夹,系统会选择默认的资源文件夹进行布局加载。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="px_1">1px</dimen>
<dimen name="px_2">2px</dimen>
<dimen name="px_3">30px</dimen>
</resources>
今日头条的屏幕适配方案
基本使用
settings.gradle
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
...
maven { url "https://jitpack.io" }
}
}
build.gradle
implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1'
在 AndroidManifest 中填写设计图尺寸,单位是 dp
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.My_study"
tools:targetApi="31">
...
<meta-data
android:name="design_width_in_dp"
android:value="360" />
<meta-data
android:name="design_height_in_dp"
android:value="640" />
</application>
</manifest>
配置完成,是的,就是这么简单。这样,框架就可以对项目中的所有页面进行适配,不过,需要注意的是,在我们开发阶段,布局的实时预览很重要,Android Studio 默认的预览可能不能完全符合我们的设计图,这就需要我们创建模拟设备了。
原理解析
dp 转换的场景,基本上都是通过 DisplayMetrics 来计算的,所以,想要满足适配,我们只需修改 DisplayMetrics 中和 dp 转换相关的变量即可。density,scaledDensity 和 densityDpi 都可以通过 displayMetrics 获得,如下所示
val density = resources.displayMetrics.density
val densityDpi = resources.displayMetrics.densityDpi
//字体缩放因子,正常情况下等于 density,调节系统字体大小后会改变这个值
val scaledDensity = resources.displayMetrics.scaledDensity
dp 和 px 的转换公式:px = dp * density
如果设计图宽为 360dp,想要保证在所有设备计算得出的 px 值都正好是屏幕宽度的话,我们只能修改 density 的值。以宽为维度来适配,那么适配后的 density 就应该是:
density = 设备真实宽(px)/ 设计图宽度(dp)