一种极低成本的Android屏幕适配方式 - 字节方案

一、屏幕适配到底在适配什么?

很多人一提到 Android 屏幕适配,第一反应是:

不同手机分辨率不一样,怎么让页面看起来一样?

但现在这个理解已经不够了。

现代 Android 屏幕适配要处理的不只是分辨率,还包括:

适配对象

说明

屏幕尺寸

小屏手机、大屏手机、平板、折叠屏

屏幕密度

mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi

横竖屏

竖屏单栏,横屏可能需要双栏

系统栏

状态栏、导航栏、手势导航区域

异形屏

刘海屏、挖孔屏、水滴屏

多窗口

分屏、小窗、ChromeOS 桌面窗口

字体缩放

用户调整系统字体大小

折叠屏状态

展开、折叠、半折叠

所以现在的屏幕适配,本质上不是“所有设备显示完全一样”,而是:

在不同屏幕、不同密度、不同窗口形态下,让 UI 都合理、可用、舒服。

Android 官方现在也更强调应用需要适配不同的 screen size、density 和 window configuration,因为应用看到的不是设备真实屏幕,而是当前可用窗口区域。Android 官方屏幕兼容说明

二、最基础的适配:dp、sp、density

Android 最早为了解决不同分辨率和不同密度的问题,提供了两个非常核心的单位:

单位

用途

dp

用于布局尺寸

sp

用于文字大小

px

真实像素,一般不直接用于布局

比如同样是 100px,在低密度屏幕上看起来可能很大,在高密度屏幕上看起来可能很小。

所以 Android 引入了 dp

dp 是与密度无关的像素单位,系统会根据屏幕 density 自动换算成真实 px。

文字使用 sp,是因为 sp 不仅会根据 density 换算,还会受到用户系统字体大小影响。

官方也明确建议:布局尺寸使用 dp,文字大小使用 sp,不要直接使用 px官方 density 适配说明

三、图片资源适配

Android 还提供了不同 density 的图片资源目录:

目录

对应密度

drawable-mdpi

1x

drawable-hdpi

1.5x

drawable-xhdpi

2x

drawable-xxhdpi

3x

drawable-xxxhdpi

4x

以前做 Android 开发,经常需要 UI 设计师切多套图。

现在则更推荐:

  1. 图标优先使用 VectorDrawable。

  2. 简单图形使用 shape 或 vector。

  3. 大图、照片、复杂图片再提供多密度资源。

  4. 服务端图片结合图片加载框架按需压缩、裁剪、缓存。

比如 Glide、Coil、Fresco 这类图片加载框架,本身也会帮我们处理很多图片采样和内存优化问题。

四、布局适配:不要写死尺寸

屏幕适配最常见的问题,不是 density,而是布局写死。

比如:

错误做法

问题

固定宽高

小屏放不下,大屏太空

固定 margin

不同屏幕比例不协调

绝对定位

横竖屏、折叠屏、多窗口容易崩

文字单行固定

字体放大后容易截断

图片固定比例不处理

容易拉伸、裁剪异常

View 体系中,比较推荐使用:

  1. ConstraintLayout

  2. LinearLayout + weight

  3. RecyclerView

  4. NestedScrollView

  5. wrap_content

  6. match_parent

  7. 0dp + constraint

  8. Guideline

  9. Barrier

  10. minWidth / maxWidth

Compose 体系中,比较推荐使用:

  1. Modifier.fillMaxWidth()

  2. Modifier.weight()

  3. BoxWithConstraints

  4. LazyColumn

  5. LazyVerticalGrid

  6. WindowSizeClass

  7. Material3 Adaptive

核心思想是:

页面应该有弹性,而不是按照某一台手机的设计稿绝对定位。

五、资源限定符适配

Android 原生提供了资源限定符机制,可以根据不同条件加载不同资源。

常见目录如下:

目录

作用

layout

默认布局

layout-land

横屏布局

layout-sw600dp

最小宽度达到 600dp,常用于平板

values

默认尺寸、颜色、样式

values-sw360dp

小屏手机尺寸

values-sw600dp

平板尺寸

drawable-xhdpi

xhdpi 图片

drawable-xxhdpi

xxhdpi 图片

其中 swsmallest width 的意思。

比如:

限定符

含义

sw360dp

设备最小宽度至少 360dp

sw600dp

设备最小宽度至少 600dp

sw720dp

设备最小宽度至少 720dp

以前很多项目会生成大量 values-swXXXdp,比如:

目录

values-sw320dp

values-sw360dp

values-sw375dp

values-sw400dp

values-sw411dp

values-sw480dp

这种方案能解决一部分问题,但也有明显缺点:

  1. 资源文件太多。

  2. 维护成本高。

  3. 容易和系统字体缩放冲突。

  4. 对折叠屏、多窗口适配不够自然。

  5. 本质上还是在追求“等比例缩放”。

所以现在可以用,但不建议滥用。

六、历史上的屏幕适配方案

1. 直接使用 px

早期有些项目会直接根据设计稿写 px

这种方式最简单,但问题最大。

因为 Android 设备密度差异很大,同样的 100px 在不同设备上视觉大小完全不同。

现在基本不推荐这种写法。

2. dp + 多套 drawable

这是 Android 官方最基础、最稳定的方案。

它解决了两个问题:

问题

解决方式

控件尺寸不一致

使用 dp

图片在不同密度下模糊

提供多套 drawable

这个方案到现在仍然是基础。

不管使用什么框架,dp / sp / drawable density 都是 Android 适配的底层逻辑。

3. 多套 layout

为了适配横屏、平板、大屏,Android 允许放多套布局。

比如:

目录

场景

layout

手机默认布局

layout-land

横屏布局

layout-sw600dp

平板布局

这种方式适合页面结构真的不同的场景。

比如手机上是单栏:

  1. 先进入列表页。

  2. 点击后进入详情页。

而平板上可以是双栏:

  1. 左边显示列表。

  2. 右边显示详情。

这种适配不是简单放大,而是重新组织页面结构。

4. weight 和百分比布局

以前很多页面会用 LinearLayout + weight 做比例布局。

后来 Android 也出现过 PercentRelativeLayoutPercentFrameLayout 这类百分比布局。

它们的思想是:

控件宽高不写死,而是按父容器比例分配。

这种方案在简单页面里有效,但复杂页面容易变得难维护。

后来随着 ConstraintLayout 普及,百分比布局基本就不再是主流了。

5. ConstraintLayout

ConstraintLayout 是 View 体系里非常重要的适配方案。

它适合处理复杂页面,并且可以减少布局嵌套。

常见能力包括:

能力

作用

约束

控制控件相对位置

Guideline

按比例或固定位置辅助布局

Barrier

根据多个控件动态确定边界

Chain

控制一组控件的排列

0dp

配合约束实现自适应宽高

ConstraintLayout 的核心价值是:

用约束关系替代绝对位置,让布局可以根据屏幕变化自动调整。

6. AutoLayout 类方案

早期也有一些 AutoLayout 方案,核心思想是:

按照设计稿尺寸,把所有控件等比例缩放。

比如设计稿是 360dp 宽,当前设备是 400dp 宽,那么所有尺寸都按比例放大。

这种方案接入简单,但缺点也明显:

  1. 容易破坏 Android 原生 dp 体系。

  2. 文字缩放不自然。

  3. 大屏设备只是简单放大,没有真正利用空间。

  4. 平板、折叠屏、多窗口适配不好。

  5. 复杂页面可能出现间距、字体、图片比例异常。

所以这种方案曾经流行,但现在已经不是推荐方向。

7. 今日头条 density 适配方案

今日头条方案曾经很有名。

它的大致思路是:

动态修改应用的 density,让设计稿宽度和设备宽度建立固定换算关系。

比如设计稿宽度是 360dp,那么通过修改 density,让当前设备宽度在代码里也表现为 360dp。

这样开发者按照设计稿写尺寸,看起来就能自动适配。

这个方案的优点是:

  1. 接入成本低。

  2. 对老项目改造比较快。

  3. 视觉还原设计稿比较容易。

缺点是:

  1. 修改 density 可能影响第三方 SDK 页面。

  2. 可能影响系统字体缩放。

  3. 可能和 WebView、Dialog、Toast、PopupWindow 等产生兼容问题。

  4. 大屏设备仍然只是等比例放大。

  5. 对折叠屏、多窗口不是最优解。

所以它适合某些强设计稿还原的项目,但不应该作为现代 Android 适配的首选方案。

8. AndroidAutoSize

AndroidAutoSize 是基于类似思路封装出来的适配框架。

它的优点是使用方便,老项目接入比较快。

但本质上还是属于“按设计稿等比例适配”的路线。

如果是老项目,页面很多、历史包袱重,可以考虑用它降低改造成本。

如果是新项目,更建议优先使用官方适配方式。

七、现代 Android 的新问题:大屏、折叠屏、多窗口

以前我们说屏幕适配,主要是在说:

这个页面在不同手机上别变形。

现在不一样了。

Android 应用可能运行在:

  1. 普通手机。

  2. 大屏手机。

  3. 平板。

  4. 折叠屏。

  5. 横屏设备。

  6. ChromeOS。

  7. 分屏窗口。

  8. 桌面自由窗口。

这时如果还只是等比例缩放,就会出现问题。

比如一个登录按钮在手机上宽度是 320dp,看起来正常。

到了平板上,如果简单放大成 700dp,就会显得非常奇怪。

所以大屏适配的核心不是“放大”,而是“重排”。

八、Window Size Class:现代推荐方案

现在官方更推荐使用 Window Size Class。

Window Size Class 不是判断设备是不是平板,而是判断当前应用窗口有多大。

这点非常重要。

因为同一个平板:

  1. 全屏时可能是 expanded。

  2. 分屏时可能是 medium。

  3. 小窗时可能是 compact。

官方也说明,Window Size Class 是根据应用当前可用窗口大小分类,而不是根据设备物理屏幕分类。Window Size Class 官方说明

可以简单理解为:

窗口宽度类型

常见 UI

Compact

手机单栏布局

Medium

可使用导航栏、局部双栏

Expanded

平板双栏、多栏布局

Large / Extra Large

桌面级复杂布局

典型页面可以这样设计:

flowchart TD A[获取当前窗口宽度] --> B{Window Size Class} B --> C[Compact: 单栏布局] B --> D[Medium: 导航栏或局部双栏] B --> E[Expanded: 列表详情双栏] B --> F[Large: 多面板布局]

比如列表详情页:

窗口类型

页面形态

Compact

列表页和详情页分开

Medium

可以尝试左右分栏

Expanded

左侧列表,右侧详情

Large

左侧导航,中间列表,右侧详情

这才是真正适合大屏、折叠屏、多窗口的适配方式。

九、全面屏、刘海屏和 Insets 适配

现在 Android 设备大多是全面屏,适配时还要考虑:

  1. 状态栏。

  2. 导航栏。

  3. 手势导航区域。

  4. 刘海区域。

  5. 挖孔区域。

  6. 瀑布屏边缘。

  7. 折叠屏铰链区域。

以前很多项目处理沉浸式状态栏,喜欢直接设置:

  1. 状态栏透明。

  2. 导航栏透明。

  3. 给根布局加 paddingTop。

  4. 手动获取 statusBarHeight。

这种方式在早期还算常见,但现在不够稳。

现代方案应该使用 WindowInsets。

官方现在推荐 edge-to-edge 设计:背景可以绘制到系统栏后面,但文本、按钮、输入框等关键内容要避开系统栏和异形区域。官方 edge-to-edge 指南

可以理解为:

内容类型

是否可以延伸到系统栏后面

背景图

可以

背景色

可以

分割线

可以

文本

不建议

按钮

不建议

输入框

不建议

列表内容

可以滚动到后面,但首尾需要 inset

十、字体缩放也属于屏幕适配

很多项目容易忽略系统字体大小。

用户可以在系统设置里把字体调大。

如果页面没有适配,会出现:

  1. 文本被截断。

  2. 按钮文字显示不全。

  3. 标题遮挡内容。

  4. 弹窗内容溢出。

  5. 列表 item 高度不够。

所以文字适配要注意:

  1. 文字使用 sp

  2. 不要过度限制单行。

  3. 重要文案允许换行。

  4. 按钮高度不要太死。

  5. 弹窗内容可以滚动。

  6. 测试系统大字体模式。

十一、现在项目应该怎么选方案?

1. 新项目

推荐方案:

  1. 使用 dp / sp

  2. 使用 ConstraintLayout 或 Compose 自适应布局。

  3. 图标使用 VectorDrawable。

  4. 图片按需提供多密度资源。

  5. 使用 sw600dp 适配平板基础布局。

  6. 使用 Window Size Class 做大屏、折叠屏、多窗口适配。

  7. 使用 WindowInsets 处理状态栏、导航栏、刘海屏。

  8. 不要依赖全局 density 修改方案。

2. 老项目

如果老项目页面很多,历史布局写死严重,可以分阶段处理:

  1. 新页面使用现代方案。

  2. 核心页面逐步改成 ConstraintLayout 或 Compose。

  3. 复杂大屏页面单独做 sw600dp 或 Window Size Class。

  4. 如果短期改不动,可以临时使用 AndroidAutoSize 类方案兜底。

  5. 不建议全局粗暴修改所有页面,容易引入兼容问题。

3. 强设计稿还原项目

比如活动页、营销页、启动页,对视觉还原要求很高,可以考虑:

  1. 使用约束布局。

  2. 使用百分比约束。

  3. 对局部页面使用等比例适配。

  4. 复杂视觉页面用 H5 或 Compose 单独处理。

  5. 不建议把全 App 都变成设计稿缩放模型。

4. 平板和折叠屏项目

重点不是缩放,而是重排。

推荐:

  1. 列表详情双栏。

  2. 左侧导航栏。

  3. 内容区域最大宽度限制。

  4. 避免按钮、输入框无限拉宽。

  5. 横屏状态下重新组织信息层级。

  6. 使用 Window Size Class,而不是简单判断设备型号。

十二、屏幕适配的核心原则

总结下来,Android 屏幕适配有几个原则:

  1. 不要直接使用 px

  2. 布局尺寸用 dp

  3. 字体大小用 sp

  4. 不要用固定尺寸堆页面。

  5. 不要指望一套设计稿适配所有设备。

  6. 小屏优先保证内容可用。

  7. 大屏优先重新组织布局。

  8. 异形屏和系统栏用 WindowInsets。

  9. 平板、折叠屏、多窗口用 Window Size Class。

  10. 第三方适配框架可以用来救老项目,但不是现代首选方案。

十三、最后总结

Android 屏幕适配经历过几个阶段:

阶段

核心思路

早期

用 px 写死

基础阶段

dp、sp、多密度图片

资源适配阶段

多套 layout、多套 values

比例适配阶段

百分比、AutoLayout、density 修改

现代阶段

自适应布局、WindowInsets、Window Size Class

现在最推荐的方案不是找一个万能框架,而是组合使用官方能力:

dp / sp 解决密度问题,弹性布局解决伸缩问题,资源限定符解决结构差异,WindowInsets 解决全面屏和异形屏,Window Size Class 解决大屏、折叠屏、多窗口。

一句话概括:

以前的屏幕适配追求“等比例还原设计稿”,现在的屏幕适配追求“根据窗口空间重新组织界面”。