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 很难优雅实现。

核心原则:CarTheme 的定制,本质上是把「视觉变量」抽象成属性,然后在不同主题下赋予不同值。这样换主题时,所有控件自动刷新,不用到处改代码。

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% 以上。

注意:间距不要用硬编码的 dp 值。一旦设计稿改了间距,你要改几十个布局文件,那滋味可不好受。

5.5 知识体系总览

下面这张图,是我对本章内容的一个梳理。你可以看到,主题定制、深色模式、字体、间距,这四个部分其实是环环相扣的。主题决定了颜色和字体,深色模式是主题的一个分支,而间距规范则是让所有界面保持一致的「粘合剂」。

车载主题与样式 CarTheme 定制 深色模式适配 字体规范 定义主题属性 Light / Dark 双主题 车厂定制色系 对比度 4.5:1 ~ 7:1 避免纯黑背景 图标重新着色 无衬线字体 最小字号 14sp 固定字体缩放 间距规范(8dp 网格)

你看,这四个模块其实是一个整体。主题定制是「骨架」,深色模式是「外衣」,字体是「五官」,间距是「比例」。任何一个环节出问题,整个界面都会「不协调」。

好了,关于主题与样式,我就讲这么多。记住一句话:车载 UI 的第一原则是安全,第二原则是统一。只要把这两点抓住了,你的界面就不会差到哪里去。

公众号:蓝海资料掘金营,微信deep3321