车载Launcher开发:从架构到定制

Launcher,说白了就是车载系统的「门面」。用户一上车,第一眼看到的就是它。我做了这么多年车载开发,可以说Launcher的体验直接决定了用户对这套系统的第一印象。今天我们就来聊聊车载Launcher的开发,从架构设计到多屏适配,再到快捷控制面板,把这块硬骨头啃下来。

Launcher架构:别急着写代码

很多人一上来就撸代码,结果后面改得想哭。我个人习惯,先画好架构图再动手。车载Launcher的架构,其实可以拆成三层:

  • 数据层:负责管理应用列表、快捷方式、壁纸等数据源
  • 逻辑层:处理交互逻辑、页面切换、状态管理
  • UI层:渲染界面,处理触摸事件

为什么要分层?说白了就是为了解耦。你想想看,如果UI和逻辑混在一起,换一个屏幕尺寸就得重写一大片代码,那不得累死?

核心原则:Launcher架构要遵循「数据驱动UI」的模式。数据变了,UI自动更新。千万别搞反了。

数据层 (Data Layer) AppListRepository | ShortcutManager | WallpaperProvider | SettingsStore 逻辑层 (Logic Layer) LauncherViewModel | PageController | NavigationManager | StateMachine UI层 (UI Layer) CarLauncherFragment | AppGridFragment | QuickControlsPanel | WidgetHost

CarLauncher与AppGrid:两个核心组件

CarLauncher和AppGrid,是车载Launcher里最核心的两个东西。CarLauncher负责主界面,AppGrid负责应用列表。我刚开始做的时候,总想把它们揉在一起,后来发现分开才是正道。

CarLauncher:主界面的设计思路

CarLauncher通常包含这几个区域:

  • 状态栏:显示时间、信号、电量等信息
  • 快捷卡片:音乐、导航、天气等常用功能
  • 底部Dock栏:常用应用快捷入口
  • 壁纸/主题:背景展示

嗯,这里要注意一点:CarLauncher的布局要考虑到驾驶安全。按键要够大,间距要够宽,不能让驾驶员分心去点小按钮。我曾经见过一个项目,把按钮做得很小很精致,结果被安全评审打回来重做,教训深刻啊。

我的经验:CarLauncher的触摸目标尺寸建议不小于64dp,间距不小于8dp。这是Android Automotive的官方建议,也是血的教训换来的。

AppGrid:应用列表的展示逻辑

AppGrid说白了就是所有应用的网格展示。但车载和手机不一样,车载的应用列表要考虑:

  • 应用数量通常较少(20-50个)
  • 需要支持分类(导航、音乐、工具等)
  • 支持搜索和排序
  • 支持隐藏/禁用某些应用

代码实现上,我推荐用RecyclerView配合GridLayoutManager。为什么?因为灵活。你可以轻松调整列数、间距、动画效果。

// AppGrid的核心实现片段
class AppGridFragment : Fragment() {
    private lateinit var appAdapter: AppAdapter
    private lateinit var appViewModel: AppViewModel
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        val recyclerView = view.findViewById<RecyclerView>(R.id.app_grid)
        recyclerView.layoutManager = GridLayoutManager(context, 4) // 4列
        recyclerView.addItemDecoration(GridSpacingItemDecoration(16)) // 间距
        
        appAdapter = AppAdapter { appInfo ->
            // 点击启动应用
            startActivity(appInfo.intent)
        }
        recyclerView.adapter = appAdapter
        
        // 观察数据变化
        appViewModel.appList.observe(viewLifecycleOwner) { apps ->
            appAdapter.submitList(apps)
        }
    }
}

多屏Launcher适配:别被屏幕数量吓到

车载系统最头疼的就是多屏适配。仪表盘、中控屏、副驾屏、后排屏……每个屏幕的尺寸、分辨率、交互方式都不一样。我刚开始做多屏适配时,差点被搞崩溃。

后来我总结了一套方法:

  1. 使用ConstraintLayout:相对布局,自适应强
  2. 定义尺寸资源:不同屏幕用不同的dimens.xml
  3. Fragment化:每个屏幕区域用独立的Fragment
  4. 统一ViewModel:数据共享,UI独立

避坑指南:我曾经在一个项目里,给每个屏幕单独写了一套布局文件,结果后期维护量爆炸。后来改成用Fragment+ViewModel的模式,一个数据源驱动多个UI,清爽多了。

多屏适配的核心,其实就是「数据统一,UI分离」。你想想看,仪表盘和中控屏显示的是同一个车速数据,但展示方式完全不同。仪表盘用指针,中控屏用数字。这不就是典型的数据统一、UI分离吗?

屏幕类型 典型分辨率 适配策略
仪表盘 1920x720 横屏、宽高比大、注重驾驶信息
中控屏 1280x720 横屏、触摸交互、功能丰富
副驾屏 1280x720 横屏、娱乐为主、独立交互
后排屏 1920x1080 横屏/竖屏、影音娱乐

快捷控制面板(Quick Controls)开发

快捷控制面板,就是用户从屏幕顶部下滑或者从侧边滑出的那个控制中心。音量、亮度、蓝牙、WiFi、驾驶模式切换……这些高频操作都放在这里。

开发快捷控制面板,有几个关键点:

  • 手势识别:下滑、侧滑、长按等手势的识别
  • 动画效果:展开/收起要有流畅的过渡动画
  • 状态同步:控制项的状态要和系统实时同步
  • 可配置性:支持用户自定义快捷开关的顺序

我个人习惯用BottomSheetBehavior来实现快捷控制面板的展开和收起。配合自定义的动画插值器,效果非常丝滑。

// 快捷控制面板的核心逻辑
class QuickControlsPanel(context: Context, attrs: AttributeSet?) : 
    LinearLayout(context, attrs) {
    
    private val controls = mutableListOf<QuickControl>()
    
    fun addControl(control: QuickControl) {
        controls.add(control)
        // 动态添加控制项视图
        val view = LayoutInflater.from(context)
            .inflate(R.layout.item_quick_control, this, false)
        view.findViewById<ImageView>(R.id.icon).setImageResource(control.icon)
        view.findViewById<TextView>(R.id.label).text = control.label
        view.setOnClickListener { control.onClick() }
        addView(view)
    }
    
    fun updateControlState(id: String, isActive: Boolean) {
        // 更新某个控制项的状态
        controls.find { it.id == id }?.let { control ->
            control.isActive = isActive
            // 更新UI
        }
    }
}

小技巧:快捷控制面板的动画时长建议控制在200-300ms之间。太短了显得生硬,太长了用户会觉得卡顿。这个数值是我在多个项目中试出来的黄金区间。

还有一个容易被忽略的点:快捷控制面板要支持「半展开」和「全展开」两种状态。半展开显示最常用的几个开关,全展开显示所有控制项。这样既方便快速操作,又不占用太多屏幕空间。

好了,关于车载Launcher开发的核心内容就聊到这里。从架构设计到具体实现,从单屏到多屏适配,再到快捷控制面板,每一步都有坑,但也都有解法。做车载开发就是这样,细节决定成败,经验积累多了,自然就顺手了。

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