如何确保应用在用户开启“大字体”模式时仍能正常显示?
解读
国内 ROM(MIUI、EMUI、ColorOS 等)把“字体大小”与“显示大小”两级调节都开放给用户,且部分机型支持 200% 以上超大字号。面试时,面试官想确认两点:
- 候选人是否真正在国产机上做过适配,而不是只在 AOSP 模拟器上跑通;
- 能否把“看得见”与“点得着”同时解决,即文字不截断、按钮不被顶出屏幕,且交互热区不低于 48 dp。
因此回答要围绕“sp 与 dp 混用、布局弹性、运行时重绘、无障碍闭环、线上验证”五个维度展开,体现工程化思维。
知识点
- sp 与 dp 的区别:sp 会随系统字体缩放,dp 只随屏幕密度变化。
- 配置变更:fontScale 属于 android:configChanges 的“font”项,默认触发 Activity 重建。
- 布局弹性:ConstraintLayout + Guidelines、LinearLayout 权重、ScrollView 嵌套、minWidth/minHeight 约束。
- 文本容器:TextView 的 maxLines、ellipsize、autoSizeTextType(Autosizing),以及 androidx.appcompat.widget.AppCompatTextView 的兼容行为。
- 热区保障:View 的 minimumTouchTargetSize(API 31 引入)或自定义 TouchDelegate,确保 48×48 dp。
- 运行时监听:Jetpack Compose 的 LocalDensity、LocalConfiguration,或 View 体系的 ComponentCallbacks.onConfigurationChanged。
- 国内验证矩阵:小米“巨无霸字体+显示大小最大”、华为“长辈模式”、OPPO“无极调节”、三星“高对比度字体”,需覆盖 1080P、1.5k、折叠屏内屏。
- 自动化:UiAutomator 截图比对 + OCR 文字识别,集成到 CI,防止回归。
答案
“我们分五步保证大字体下正常显示:
第一步,设计阶段就强制规范——字号全部用 sp,边距用 dp,禁止硬编码 px;同时把按钮高度、图标最小边设置为 48 dp,防止热区缩水。
第二步,布局必须可拉伸:所有页面外层套 ScrollView 或 NestedScrollView,关键区域用 ConstraintLayout 0dp 链或 LinearLayout 权重,保证字体放大后高度被撑开也不截断;对卡片列表开启 recyclerView.setHasFixedSize(false) 让 Item 自动增高。
第三步,文字自身要会“缩”:对可能超长的大字号场景,打开 TextView 的 autoSizeTextType=”uniform”并设置 minTextSize=12sp、maxTextSize=22sp,让系统在极限字号时自动回退,避免折行溢出;标题等不允许缩放的区域,则通过 maxLines=2 + ellipsize=end 做兜底。
第四步,运行时监听配置变化:在 Activity 的 onConfigurationChanged 中判断 newConfig.fontScale,若发现用户从正常 1.0 切到 1.3 以上,就主动调用 recreate() 重走生命周期,保证 Compose 的 Density 重新计算、View 的 sp 重新换算;同时把 fontScale 写入 MMKV,作为埋点上报,方便后台统计异常页面。
第五步,上线前做国产机矩阵验证:在小米、华为、OPPO、vivo 各取一台低分辨率机器,把系统字体和显示大小都拉到最大,跑一遍主路径截图;用自研的 OCR-diff 工具比对文字完整度,截断率超过 1% 就打回;灰度期间在 Google Play Console 和国内渠道后台过滤关键字‘字体’、‘显示不全’,出现差评立即回滚并补发 hotfix。
通过这五步,我们上一次大版本发版后,因大字体导致的差评从 0.9% 降到 0.05%,实现用户无感知兼容。”
拓展思考
- 折叠屏展开态下,屏幕密度不变但可用宽度骤增,若继续用 sp 会导致字号“看起来”变小,是否考虑动态计算参考字号?
- 车载和 Wear OS 的“高对比度字体”会把系统字体换成全大写,且字宽增加 15%,如何保持表盘和 HUD 不换行?
- 如果产品坚持“像素级还原设计稿”,可否通过 Density 手动缩放 dp 场,而非禁止用户放大字体?这种方案对 Compose 的 LocalDensity 和 MaterialTheme.typography 会带来哪些副作用?