文章

屏幕适配方案

屏幕适配方案

屏幕适配方案概述

image.png

1、系统布局

  • match_parent, wrap_content
  • LinearLayout 的 weight
  • RelativeLayout
  • ConstraintLayout 的 constraint

2、单位(dp/sp 方案)

  • dp 一般适配控件宽高
  • sp 一般设配字体
  • dp 方案缺点:
    相同分辨率的手机,尺寸不同,dpi 不同,density 不同,导致 1dp 代表的像素不一样,导致部分机型适配不友好

dp 适配差异性

在 dp 差异很大的情况下,dp 适配差异还是很大的

3、.9 图

4、限定符

限定符就是 Android 在进行资源加载的时候会按照屏幕的相关信息对文件夹对应的名字进行识别,而这些特殊名字就是我们的限定符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
限定符分类
    屏幕尺寸    
        small   小屏幕
        normal  基准屏幕
        large   大屏幕
        xlarge  超大屏幕
    屏幕密度
        ldpi    <=120dpi
        mdpi    <= 160dpi
        hdpi    <= 240dpi
        xhdpi   <= 320dpi
        xxhdpi  <= 480dpi
        xxhdpi  <= 640dpi(只用来存放icon)
        nodpi   与屏幕密度无关的资源.系统不会针对屏幕密度对其中资源进行压缩或者拉伸
        tvdpi   介于mdpi与hdpi之间,特定针对213dpi,专门为电视准备的,手机应用开发不需要关心这个密度值.
    屏幕方向    
        land    横向
        port    纵向
    屏幕宽高比   
        long    比标准屏幕宽高比明显的高或者宽的这样屏幕
        notlong 和标准屏幕配置一样的屏幕宽高比

4.1 尺寸限定符

res/layout-large/main.xml

这种通过 large 限定符分辨屏幕尺寸的方法,适用于 android3.2 之前。在 android3.2 之后,为了更精确地分辨屏幕尺寸大小,Google 推出了最小宽度限定符

4.2 宽高限定符

需要适配各种分辨率,values-aaaxbbb
如:values-1920x1080

  • 缺点
    分辨率太多,不好适配众多分辨率,还需要考虑虚拟导航键;需要精准命中分辨率,容错机制很差

4.3 屏幕方向限定符

给横屏、竖屏显示的布局不一样。就可以使用屏幕方向限定符来实现

1
2
3
res/values-sw600dp-land/layouts.xml:横屏

res/values-sw600dp-port/layouts.xml:竖屏

4.4 最小宽度限定符 dimens dp(sw 方案)

最小宽度限定符的使用和 large 基本一致,只是使用了具体的宽度限定。
res/layout-sw600dp/main.xml
最小宽度限定符适用于 android3.2 之后,所以如果要适配 android 全部的版本,就要使用 large 限定符和 sw600dp 文件同时存在于项目 res 目录

AS 插件 ScreenMatch 自动生成 dimens 文件夹

比例

  • 百分比布局 已经过时
  • 自定义 ratio 控件
    hongyang 的AutoLayout,基于一个具体的设计图纸尺寸,根据目标设备的尺寸来进行比例适配

今日头条方案

修改 density,保证每个设备的屏幕总 dp 宽度不变
如以 360dp 为基准,然后根据 widthPixels/360dp = density,然后设置到系统 DisplayMetrics 去

https://github.com/weiyanjie/SmartLayout

AutoLayout

AutoLayout 坑

  1. 不继承 view 的控件例如:alertDialog 或者 popupwindow 无法适配
  2. 这个适配方案没有考虑 statusbar 和 navigation bar。在带虚拟按键的手机上面会很明显。谷歌 nexus 和华为大部分机型都是自带虚拟按键的,我们可以简单的测试一下。就加载一个圆形的头像,你会发现头像被压扁了

屏幕限定符

宽高限定符

px 适配

如以 1280x720 为基准,把所有的宽度切成 720 份,高度切成 1280 份,其他的尺寸基于这个基准等比缩放
1280x720,values-1280x720

如果是 800x400,values-800x480
根据不同的屏幕,x720 变量会代表对应的 px 值,其他的变量也一样

步骤

  1. 选一个尺寸为基准,一般是设计师的设计图纸,如 1920x1080
  2. 高就分 1920 份,宽就分 1080 分,其他的尺寸根据这个缩放
  3. 如 1280x720,将 1280 分成 1920 份,720 分成 1080 份
1
2
3
4
5
6
7
在1280x720分辨率高度1px = 1280/1920 = 0.667px
在1280x720分辨率宽度1px = 720/1080 = 0.667px

由于宽高比例一样,那么可以用一套
1x = 0.667px
1080x = 720px
1920x = 1280px
  1. UI 使用,直接根据设计师根据的基准分辨率 1920x1080 的设计图的标注,如按钮宽高 48x48px,那么直接在控件填 48x,系统会根据当前设备的尺寸自动寻找对应的 dimens 来加载。

最小宽度限定符 smallWidth(sw 方案)

sw 适配特点

  1. 目前主流设备都是 360dp,可以覆盖 90%
  2. 不能适配高,只是基于宽,或者只能基于高或者宽,why?请看:

https://github.com/JessYanCoding/AndroidAutoSize/issues/8

sw 适配 dimens 生成插件

https://github.com/mengzhinan/PhoneScreenMatch

使用:Android Studio 双击 shift,输入 ScreenMatch

sw 原理

基于 google 的 sw<N>dp 限定符规则,提供多套 dimens.xml 文件

dp 适配原理和 px 适配一样,只不过是拿 dp 值来等比缩放的而已

无论手机屏幕的像素多少,密度比值多少,但 80% 的手机的 dp 值 (widthPixels / density) 都为 360dp,所以对于这些手机,我们以 360dp 为基准,即只要写@dimen/dp_360 即可让控件横向沾满屏幕。那万一有些手机的这个值是 520dp 怎么办呢?加进去生成对应的 values 即可。

sw 适配步骤

  • 根据具体的设计图纸尺寸来适配,如 1920x1080,使用直接用 px
  1. 找一个基准 dp,一般是找 360dp,即默认的 dimens 的值是根据 360dp 来的
  2. 按照 ui 设计图的设计图纸尺寸,如 1920x1080
  3. 计算 dimens,默认的就是 sw360
  4. 计算过程
1
2
3
4
5
6
7
8
9
# 360dp计算
分为1080份,
1份,px1 = 360dp/1080 = 0.33dp
px540 = 360dp/1080 = 180dp

# 410dp计算
分为1080份,
1份,px1 = 410dp/1080 = 0.3796dp
px540 = 410dp/1080 = 205dp
  1. 使用,直接在 ui 上填写基于 1920x1080 设计上标注的 px 对应的 dimens 值即可,不用转换为 dp
  • 根据 dp,使用用设计标注的 px/density

案例 - 使用 px

默认 values/dimens.xml

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--base 360dp 1920x1080-->
    <dimen name="px_200">66.67dp</dimen>
    <dimen name="px_540">180dp</dimen>
</resources>

values-sw410dp/dimens.xml

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--base 410dp 1920x1080-->
    <dimen name="px_200">75.926dp</dimen>
    <dimen name="px_540">205dp</dimen>
</resources>

values-sw600dp/dimens.xml

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--base 600dp 1920x1080-->
    <dimen name="px_200">111.111dp</dimen>
    <dimen name="px_540">300dp</dimen>
</resources>

使用:

1
2
3
4
5
<me.hacket.assistant.common.widget.CapsTextView
    android:text="适配dp 540 x 200"
    android:background="@color/amber_200"
    android:layout_width="@dimen/px_540"
    android:layout_height="@dimen/px_200"/>
  • 只适配了 360dp 效果:



小米 max3:

  • 适配了 410dp 和 360dp 效果:

很明显后面 2 个 411dp 的设备基本居中了

小米 max3:

  • 适配了 600dp、410dp、360dp 效果: image.png

小米 max3:

![image.png1000](https://raw.githubusercontent.com/hacket/ObsidianOSS/master/obsidian/202501012329943.png)

使用 dp

360dp/410dp
67dp/76.3056dp

默认 values/dimens.xml

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--base 360dp-->
    <dimen name="dp_67">67dp</dimen>
    <dimen name="dp_180">180dp</dimen>
</resources>

values-sw410dp/dimens.xml

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--base 410dp0-->
    <dimen name="dp_67">76.3056dp</dimen>
    <dimen name="dp_180">205dp</dimen>
</resources>

values-sw600dp/dimens.xml

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--base 600dp 1920x1080-->
    <dimen name="dp_67">111.6667dp</dimen>
    <dimen name="dp_180">300dp</dimen>
</resources>

使用:

1
2
3
4
5
<me.hacket.assistant.common.widget.CapsTextView
        android:text="适配dp 540 x 200"
        android:background="@color/amber_200"
        android:layout_width="@dimen/dp_180"
        android:layout_height="@dimen/dp_67"/>

小结:
无论是用 px 来划分多少份还是用 dp 来划分份数,都是一样的;使用上不一样而已,px 划分就可以直接填写设计图纸标注的 px,而用 dp 的话就需要手动转换为 dp 适配,还可能 px 转换为 dp 在生成的 dimens 找不到,只能找最近的 dimens 值

今日头条屏幕适配方案

原理

修改 density,保证每个设备的屏幕总 dp 宽度不变
如以 360dp 为基准,然后根据 widthPixels/360dp = density,算出 density,然后设置到系统 DisplayMetrics 去

核心源码:

1
2
3
4
5
6
7
val baseDp: Float = 360.0F

var displayMetrics = resources.displayMetrics
var widthPixels = displayMetrics.widthPixels
var originalDensity = displayMetrics.density
var realDensity = widthPixels / baseDp\
displayMetrics.density = realDensity

特点

优点:

  1. 侵入性低,使用成本低
  2. 兼容性高,都是使用官方 API
  3. 可适配第三方库和系统库
  4. 没有性能损耗

缺点:

  1. 个别不是按照设计图来设计的需要单独处理

思考:
屏幕 dpi 更大 不是为了显示更多的内容么?

Reference

今日头条自定义 density 的改进版 pt

利用 pt 单位来承担

屏幕适配资源

尺寸限定符 -values- 分辨率

比例/自定义控件

sw 方案

sw 生成 gradle 插件https://github.com/Tangpj/calces-gradle-plugin

今日头条方案

一种极低成本的 Android 屏幕适配方式
https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA

今日头条屏幕适配方案

  • 系列
  1. 骚年你的屏幕适配方式该升级了!-smallestWidth 限定符适配方案
    https://juejin.im/post/5ba197e46fb9a05d0b142c62
  2. 骚年你的屏幕适配方式该升级了!- 今日头条适配方案
    https://juejin.im/post/5b7a29736fb9a019d53e7ee2
  3. 今日头条屏幕适配方案终极版正式发布!
    https://juejin.im/post/5bce688e6fb9a05cf715d1c2

Android 技能树 — 屏幕适配小结
https://juejin.im/post/5b5315c8e51d45198565b172

Android 屏幕适配从未如斯简单(8 月 10 日最终更新版)
https://juejin.im/post/5b6250bee51d451918537021

基于今日头条的 pt 方案

开源方案

今日头条方案

今日头条方案

本文由作者按照 CC BY 4.0 进行授权