5、主题与样式:CarTheme定制、深色模式适配、字体与间距规范
说实话,车载系统的主题定制,跟手机App完全是两码事。手机你换个壁纸、改个强调色,用户开心就好。但在车里,主题直接关系到驾驶安全——白天阳光刺眼,晚上隧道漆黑,字体太小看不清,间距太密容易误触。这些都不是小事。
我最早做车载项目时,就吃过深色模式的亏。当时觉得「不就换个背景色嘛」,结果测试时发现,白天在强光下,深色背景上的灰色文字根本看不清。嗯,从那以后,我再也不敢小看主题适配了。
5.1 CarTheme 的定制思路
Android 原生的 Material Design 主题,在车载场景下其实不太够用。车机屏幕通常更大,分辨率更复杂,而且交互方式以触控和旋钮为主。我个人习惯的做法是:基于 Theme.MaterialComponents.DayNight 做一层封装,再叠加车载特有的属性。
举个例子,我会在 attrs.xml 里定义一套车载专属的主题属性:
<resources>
<attr name="carPrimaryColor" format="color" />
<attr name="carSurfaceColor" format="color" />
<attr name="carOnSurfaceColor" format="color" />
<attr name="carIconTint" format="color" />
<attr name="carFontScale" format="float" />
<attr name="carSpacingUnit" format="dimension" />
</resources>
然后在 themes.xml 里定义两套主题:CarTheme.Light 和 CarTheme.Dark。注意,这里不是简单继承 DayNight,而是把上面这些属性都赋值好。为什么?因为车厂经常要求「白天用蓝色调,晚上用琥珀色调」,这种需求用原生 DayNight 很难优雅实现。
5.2 深色模式适配,别只改背景色
深色模式适配,我见过太多人只改了个 ?attr/colorBackground。结果呢?白天看着还行,晚上一用,对比度太高刺眼,对比度太低看不清。
车载深色模式有几个特殊要求:
- 对比度要适中:文字与背景的对比度建议在 4.5:1 到 7:1 之间。太低了看不清,太高了夜间刺眼。
- 避免纯黑背景:纯黑 (#000000) 在 OLED 屏幕上虽然省电,但长时间看容易产生视觉疲劳。我一般用深灰 (#1E1E1E 或 #2D2D2D)。
- 图标要重新着色:白天用的彩色图标,在深色背景下可能太跳。建议用
?attr/carIconTint统一控制图标颜色。
我曾经在一个项目中,客户反馈「晚上导航界面太亮,晃眼睛」。排查后发现,是地图控件的深色模式只改了背景,但路线的颜色没变,还是高亮蓝色。解决方案很简单:把路线颜色也做成主题属性,深色模式下降低饱和度。
@color/xxx 定义颜色时,可以同时定义 light 和 dark 两个值。然后在 values-night 目录下覆盖。这样系统会自动切换,省心省力。
5.3 字体规范,关乎安全
车载系统的字体,第一要求是「可读性」,第二才是「美观」。我见过一些设计稿,用了很细的字体,看着确实高级,但一上车就完蛋——阳光一照,根本看不清。
我的建议是:
- 使用无衬线字体:比如 Roboto、Noto Sans CJK。衬线字体在低分辨率屏幕上容易糊。
- 最小字号 14sp:这是底线。导航信息、车速等关键信息,建议 18sp 以上。
- 字重至少 Medium:Regular 在强光下可能不够清晰。我习惯用 Medium 或 SemiBold。
另外,字体缩放也要注意。车机不像手机,用户不会去系统设置里调字体大小。所以,字体大小应该是固定的,不要跟随系统字体缩放。怎么做?在主题里设置:
<item name="android:fontScale">1.0</item>
嗯,这样就能保证所有界面字体大小一致,不会出现「用户调大了系统字体,结果界面布局崩了」的情况。
5.4 间距规范,统一才好看
间距这东西,看着简单,但做起来最容易乱。你想想看,一个页面里,有的间距是 8dp,有的是 10dp,有的是 12dp,视觉上就会显得很「碎」。
我习惯定义一套间距系统,类似 Material Design 的 8dp 网格:
| 名称 | 值 | 使用场景 |
|---|---|---|
| spacing_xxs | 4dp | 图标与文字之间的间距 |
| spacing_xs | 8dp | 列表项内部元素间距 |
| spacing_sm | 12dp | 卡片内边距 |
| spacing_md | 16dp | 列表项之间的间距 |
| spacing_lg | 24dp | 区块之间的间距 |
| spacing_xl | 32dp | 页面边缘间距 |
然后在布局里统一引用:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/spacing_md">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="歌曲名称"
android:textSize="16sp"
android:textStyle="bold"
android:layout_marginEnd="@dimen/spacing_xs"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/icon_favorite"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/icon_favorite"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_favorite"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
这样做的好处是,后期如果要调整间距,只需要改一个 dimens.xml 文件,所有界面自动更新。我在项目中就靠这套规范,把 UI 还原度从 70% 提到了 95% 以上。
5.5 知识体系总览
下面这张图,是我对本章内容的一个梳理。你可以看到,主题定制、深色模式、字体、间距,这四个部分其实是环环相扣的。主题决定了颜色和字体,深色模式是主题的一个分支,而间距规范则是让所有界面保持一致的「粘合剂」。
你看,这四个模块其实是一个整体。主题定制是「骨架」,深色模式是「外衣」,字体是「五官」,间距是「比例」。任何一个环节出问题,整个界面都会「不协调」。
好了,关于主题与样式,我就讲这么多。记住一句话:车载 UI 的第一原则是安全,第二原则是统一。只要把这两点抓住了,你的界面就不会差到哪里去。