Launcher 定制开发实战:从零实现移动屏功能

8 阅读2分钟

Launcher 定制开发实战:从零实现移动屏功能

背景

项目需求是做一个定制 Launcher,支持长按图标后拖动到其他屏幕。听起来简单,但实际做起来有不少细节要处理。

基于 AOSP Launcher3 改的,Android 10 系统。

需求分析

功能点:

  1. 长按图标进入编辑模式
  2. 拖动图标到屏幕边缘时自动切换到下一屏
  3. 松手后图标固定在新位置
  4. 支持文件夹拖动

技术难点:

  • 如何检测拖动到边缘
  • 如何触发翻页动画
  • 如何保存新的位置信息

源码分析

Launcher3 的拖拽流程

长按图标
  → Workspace.onLongClick()
    → DragController.startDrag()
      → 创建 DragView(拖拽时的视图)
        → 监听 onTouchEvent
          → 检测拖拽位置
            → 松手时 DragController.onDrop()

关键类:

  • Workspace:桌面容器,管理多个屏幕
  • CellLayout:单个屏幕,网格布局
  • DragController:拖拽控制器
  • DragView:拖拽时显示的视图

实现步骤

1. 检测拖动到边缘

Workspace.java 中监听拖拽位置:

@Override
public void onDragOver(DragObject d) {
    float x = d.x;
    int screenWidth = getWidth();

    // 左边缘 10% 区域
    if (x < screenWidth * 0.1f && mCurrentPage > 0) {
        scrollToPage(mCurrentPage - 1);
    }
    // 右边缘 10% 区域
    else if (x > screenWidth * 0.9f && mCurrentPage < getChildCount() - 1) {
        scrollToPage(mCurrentPage + 1);
    }
}

2. 遇到的问题:翻页太频繁

问题: 拖到边缘后,页面疯狂来回切换。

原因: onDragOver() 每次移动都会触发,导致重复调用 scrollToPage()

解决: 添加防抖逻辑

private long mLastScrollTime = 0;
private static final long SCROLL_DELAY = 300; // 300ms 防抖

@Override
public void onDragOver(DragObject d) {
    long now = SystemClock.uptimeMillis();
    if (now - mLastScrollTime < SCROLL_DELAY) {
        return; // 忽略频繁触发
    }

    float x = d.x;
    int screenWidth = getWidth();

    if (x < screenWidth * 0.1f && mCurrentPage > 0) {
        scrollToPage(mCurrentPage - 1);
        mLastScrollTime = now;
    } else if (x > screenWidth * 0.9f && mCurrentPage < getChildCount() - 1) {
        scrollToPage(mCurrentPage + 1);
        mLastScrollTime = now;
    }
}

3. 保存新位置到数据库

松手时需要更新数据库:

@Override
public void onDrop(DragObject d) {
    CellLayout targetLayout = getCurrentDropLayout();
    ItemInfo item = (ItemInfo) d.dragInfo;
    int[] targetCell = findNearestVacantArea(d.x, d.y, targetLayout);

    LauncherModel.moveItemInDatabase(mLauncher, item,
            LauncherSettings.Favorites.CONTAINER_DESKTOP,
            mCurrentPage, targetCell[0], targetCell[1]);
}

遇到的坑

坑1:拖动时页面抖动

翻页动画和拖拽冲突,导致视觉抖动。解决方法是翻页时暂停拖拽更新。

坑2:数据库更新不及时

多次快速拖动时,数据库写入有延迟,导致重启后位置错乱。改用批量更新解决。

总结

Launcher 定制开发需要熟悉 AOSP Launcher3 的代码结构。核心是理解拖拽流程和数据持久化机制。


有问题欢迎评论区交流!