App Inventor 2 手势识别扩展教程 - GestureDetector

« 返回首页

现在已有内置组件「多点触控」:双击、长按、滑动、双指缩放和旋转、多点追踪都不用再导入拓展,推荐直接使用。

概述

App Inventor 2的Canvas和Screen只有基本的Touch/Dragged/Fling事件。手势识别扩展(GestureDetector)可以识别更丰富的手势:

手势 内置支持 扩展支持
单击 ✅ Touched ✅
双击 ❌ ✅ onDoubleTap
长按 ❌ ✅ onLongPress
滑动 ✅ Flung ✅ onFling(更精确)
缩放(捏合) ❌ ✅ onScale
滚动 部分(Dragged) ✅ onScroll
多点触控 ❌ ✅ Multitouch扩展

已有文档参考

  • other/extensionsGestures.md — 手势扩展基础文档
  • other/extensionsMultitouch.md — 多点触控扩展
  • other/extensionsRotation.md — 旋转手势扩展

本文在已有文档基础上,补充具体的使用场景和集成方案。

GestureDetector扩展

导入扩展

  1. 下载 GestureDetector扩展(.aix文件)
  2. Designer → Extension → Import Extension → 选择.aix

核心事件

when GestureDetector1.DoubleTapAtPoint(X, Y) {
  // 双击触发
  标签_状态.Text = join("双击 @ ", X, ",", Y)
}
when GestureDetector1.LongClick(X, Y) {
  // 长按触发
  标签_状态.Text = join("长按 @ ", X, ",", Y)
}
when GestureDetector1.ThumbEnabled(起点X, 起点Y, 终点X, 终点Y, 速度X, 速度Y) {
  // 滑动方向判断
  var dX = 终点X - 起点X
  var dY = 终点Y - 起点Y
  if abs(dX) > abs(dY) {
    if dX > 0 {
      标签_状态.Text = "👉 右滑"
    } else {
      标签_状态.Text = "👈 左滑"
    }
  } else {
    if dY > 0 {
      标签_状态.Text = "👇 下滑"
    } else {
      标签_状态.Text = "👆 上滑"
    }
  }
}

关联组件

GestureDetector需要关联到一个可视组件(如Canvas或Image):

when Screen1.Initialize() {
  GestureDetector1.设置目标组件(画布1)
}

实战案例:图片浏览器手势

功能

  • 左右滑动切换图片
  • 双击放大/缩小
  • 长按保存图片
  • 上下滑动显示/隐藏工具栏
global 图片列表 = ["pic1.png", "pic2.png", "pic3.png", "pic4.png"]
global 当前索引 = 1
global 是否放大 = false

when Screen1.Initialize() {
  GestureDetector1.SetTarget(图像1)
  图像1.Picture = selectListItem(图片列表, 1)
}

when GestureDetector1.OnSwipe(startX, startY, endX, endY, speedX, speedY) {
  global dX = endX - startX
  if (math.abs(dX) > 50) { // 滑动距离阈值
    if ((dX > 0) and (当前索引 > 1)) {
      global 当前索引 = 当前索引 - 1
      图像1.Picture = selectListItem(图片列表, 当前索引)
    } else if ((dX < 0) and (当前索引 < lengthOfList(图片列表))) {
      global 当前索引 = 当前索引 + 1
      图像1.Picture = selectListItem(图片列表, 当前索引)
    }
    标签_页码.Text = join(当前索引, "/", lengthOfList(图片列表))
  }
}

when GestureDetector1.OnDoubleTap(X, Y) {
  if (是否放大) {
    图像1.WidthPercent = 90
    图像1.Height = -1 // Auto
    global 是否放大 = false
  } else {
    图像1.WidthPercent = 200
    图像1.HeightPercent = 200
    global 是否放大 = true
  }
}

when GestureDetector1.OnLongPress(X, Y) {
  文件增强1.CopyFile(图像1.Picture, join("/sdcard/DCIM/收藏图片_", 当前索引, ".png"))
  Notifier1.ShowMessageDialog("图片已保存到相册", "已保存", "确定")
}

实战案例:手势密码锁

功能

  • 在Canvas上画手势路径
  • 记录触摸经过的格子
  • 验证手势密码
global 网格大小 = 3 // 3x3网格
global 格子坐标 = []
global 手势路径 = []
global 正在画 = false
global 正确密码 = "1-2-3-6-9" // 示例密码路径

when Screen1.Initialize() {
  // 计算格子中心坐标
  global 格子宽 = 画布1.Width / 网格大小
  global 格子高 = 画布1.Height / 网格大小
  global i = 0
  while (i < 9) {
    global 行 = math.floor(i / 网格大小)
    global 列 = modulo(i, 网格大小)
    global 中心X = 列 * 格子宽 + 格子宽 / 2
    global 中心Y = 行 * 格子高 + 格子高 / 2
    addItemsTo(格子坐标, create_dictionary("id", (i + 1), "x", 中心X, "y", 中心Y))
    global i = i + 1
  }
  绘制网格()
}

when 画布1.TouchDown(x, y) {
  global 正在画 = true
  global 手势路径 = []
  检查格子(x, y)
}

when 画布1.Dragged(startX, startY, prevX, prevY, currentX, currentY, draggedAnySprite) {
  if (draggedAnySprite) {
    检查格子(currentX, currentY)
    // 画线连接
    画布1.DrawLine(prevX, prevY, currentX, currentY)
  }
}

procedure 检查格子(X, Y) {
  global i = 1
  while (i <= lengthOfList(格子坐标)) {
    global 格子 = selectListItem(格子坐标, i)
    global 格X = Dictionary.Get(格子, "x")
    global 格Y = Dictionary.Get(格子, "y")
    global 格ID = Dictionary.Get(格子, "id")
    global 距离 = math.sqrt((X - 格X) * (X - 格X) + (Y - 格Y) * (Y - 格Y))
    if ((距离 < 40) and (not(is_in_list(格ID, 手势路径)))) {
      addItemsTo(手势路径, 格ID)
      // 高亮选中的格子
      画布1.DrawCircle(格X, 格Y, 30, true)
    }
    global i = i + 1
  }
}

when 按钮_验证.Click() {
  global 路径文本 = join(手势路径)
  if (路径文本 == 正确密码) {
    标签_结果.Text = "✅ 密码正确"
    openAnotherScreen("Screen_Main")
  } else {
    标签_结果.Text = "❌ 密码错误"
  }
}

Multitouch多点触控

参考已有文档 other/extensionsMultitouch.md,此处补充关键用法:

global 手指计数 = 0

when Multitouch1.TouchDown(指针ID, X, Y) {
  global 手指计数 = 手指计数 + 1
  标签_手指.Text = join("触摸点:", 手指计数)
}

when Multitouch1.TouchMove(指针ID, X, Y) {
  // 多指拖拽处理
  if (手指计数 == 2) {
    // 双指缩放
    global 距离 = 计算两点距离(手指1X, 手指1Y, 手指2X, 手指2Y)
    global 缩放比 = 距离 / 初始距离
    图像1.Width = 初始宽度 * 缩放比
  }
}

when Multitouch1.TouchUp(指针ID, X, Y) {
  global 手指计数 = 手指计数 - 1
}

常见问题

Q1: 手势扩展和Canvas事件冲突?

设置优先级:手势扩展和Canvas事件可能同时触发。解决:

  • 用手势扩展处理复杂手势(双击、长按)
  • 用Canvas事件处理绘制相关
  • 设置标志变量控制

Q2: 手势识别灵敏度如何调整?

大多数手势扩展有阈值参数:

  • CONFIRM_THRESHOLD — 确认阈值
  • VELOCITY_THRESHOLD — 速度阈值
  • 根据实际体验微调

Q3: 如何实现下拉刷新?

when GestureDetector1.OnSwipe(startX, startY, endX, endY, speedX, speedY) {
  global dY = endY - startY
  if ((dY > 100) and (speedY > 500)) { // 下拉距离够且速度够
    // 触发刷新
    刷新数据()
  }
}

总结

手势 方案 推荐扩展
单击 内置Touched 不需要
双击 GestureDetector GestureDetector
长按 GestureDetector GestureDetector
滑动方向 内置Flung或扩展 GestureDetector
缩放 Multitouch Multitouch
旋转 Rotation扩展 Rotation
多指 Multitouch Multitouch
文档反馈