文章目录

  • 前言
  • dispatchGesture `api>=24`
    • GestureDescription
    • GestureResultCallback
    • 执行手势
  • DslAccessibilityGesture
    • click 点击
    • double 双击
    • move 移动
    • fling 快速移动
  • 系列文章
  • 联系作者

前言

无障碍可以执行触屏手势操作,并且非常简单.

函数签名如下api>=24:

android.accessibilityservice.AccessibilityService#dispatchGesture

官方文档地址

dispatchGesture api>=24

GestureDescription

构建手势:

  • 声明一个构造器
val gestureBuilder = GestureDescription.Builder()
  • 创建一个手势
val path = Path()//如果是点击/双击手势
path.moveTo(x, y)//如果是移动/快速手势
path.moveTo(fromX, formY)
path.lineTo(toX, toY)
val stroke = GestureDescription.StrokeDescription(path,startTime,duration)//startTime 手势开始的时间延迟, 毫秒
//duration 手势持续的时间, 毫秒//如果需要快速滑动效果 duration 设置成一个小值
  • 添加手势
gestureBuilder.addStroke(stroke)//如果需要双击
val stroke1 = GestureDescription.StrokeDescription(path,startTime,duration)
val stroke2 = GestureDescription.StrokeDescription(path,startTime+60,duration)gestureBuilder.addStroke(stroke1)
gestureBuilder.addStroke(stroke2)//构建
gestureBuilder.build()

GestureResultCallback

手势执行回调.

val gestureResultCallback = object : AccessibilityService.GestureResultCallback() {override fun onCancelled(gestureDescription: GestureDescription?) {super.onCancelled(gestureDescription)//手势取消}override fun onCompleted(gestureDescription: GestureDescription?) {super.onCompleted(gestureDescription)//手势完成}
}

执行手势

dispatchGesture方法必须在主线程调用.

如果未指定Handler, gestureResultCallback默认在主线程回调.

如果上一个手势还未执行完成, 下一个手势就触发了, 则上一个手势会被中断.

如果用户干预了手势执行, 手势也会被中断.

如果在主线程执行手势, 那么主线程卡顿时, 也会影响手势执行的结果.

service.dispatchGesture(gestureBuilder.build(),gestureResultCallback,null)

DslAccessibilityGesture

这里有一份我封装的手势操作类, kotlin语言编写.

typealias GestureResult = (gestureDescription: GestureDescription? /*执行的手势*/, dispatched: Boolean /*是否发送*/, canceled: Boolean /*是否被取消*/) -> Unit@TargetApi(Build.VERSION_CODES.N)
class DslAccessibilityGesture {companion object {//开始时间const val DEFAULT_GESTURE_START_TIME = 16L//点击时长const val DEFAULT_GESTURE_CLICK_DURATION = 16L//双击间隔时长const val DEFAULT_GESTURE_DOUBLE_DURATION = 60L//如果Duration时间太短, 将会产生flingconst val DEFAULT_GESTURE_MOVE_DURATION = 600Lconst val DEFAULT_GESTURE_FLING_DURATION = 30L //值太大, 将没有fling效果}/**执行回调*/var gestureResult: GestureResult? = nullvar startTime: Long = DEFAULT_GESTURE_START_TIMEvar duration: Long = DEFAULT_GESTURE_MOVE_DURATIONvar clickDuration: Long = DEFAULT_GESTURE_CLICK_DURATIONvar doubleDuration: Long = DEFAULT_GESTURE_DOUBLE_DURATIONvar willContinue: Boolean = false/**无障碍服务, 用于执行手势*/var service: AccessibilityService? = null/*** 用于构建手势, 支持多指触控* [android.accessibilityservice.GestureDescription.getMaxStrokeCount]* */var _gestureBuilder: GestureDescription.Builder? = nullvar _gestureResultCallback: AccessibilityService.GestureResultCallback? = null//是否发送了事件var _isDispatched: Boolean = false/**是否执行完成*/var _isCompleted: Boolean = falsevar _countDownLatch: CountDownLatch? = null//是否已经有手势在执行var _isDo: Boolean = falseinit {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {_gestureResultCallback = object : AccessibilityService.GestureResultCallback() {override fun onCancelled(gestureDescription: GestureDescription?) {super.onCancelled(gestureDescription)L.d("手势取消:$gestureDescription ${gestureDescription?.strokeCount ?: 0}".apply {//AutoParseInterceptor.log(this)})_isCompleted = falsegestureResult?.invoke(gestureDescription, true, true)clear()}override fun onCompleted(gestureDescription: GestureDescription?) {super.onCompleted(gestureDescription)L.d("手势完成:$gestureDescription ${gestureDescription?.strokeCount ?: 0}".apply {//AutoParseInterceptor.log(this)})_isCompleted = truegestureResult?.invoke(gestureDescription, true, false)clear()}}}}fun clear() {_isDo = false_isDispatched = false_gestureBuilder = nullgestureResult = null_countDownLatch?.countDown()_countDownLatch = null}/**开始执行手势*/fun doIt(): Boolean {if (_isDo) {return false}_isDispatched = false_isCompleted = falseval service = serviceval builder = _gestureBuildertry {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && service != null && builder != null) {//设备支持手势_isDo = truereturn if (isMain()) {_isDispatched = service.dispatchGesture(builder.build(),_gestureResultCallback,null)L.w("派发手势:$_isDispatched")_isDispatched} else {MainExecutor.execute {_isDispatched = service.dispatchGesture(builder.build(),_gestureResultCallback,null)L.w("派发手势:$_isDispatched")}_countDownLatch = CountDownLatch(1)_countDownLatch?.await()_isCompleted}//AutoParseInterceptor.log("派发手势:$_isDispatched")} else {//设备不支持手势gestureResult?.invoke(null, false, true)//AutoParseInterceptor.log("设备不支持手势")L.w("设备不支持手势")return true}} catch (e: Exception) {clear()e.printStackTrace()L.w("手势异常${e.stackTraceToString()}")return false}}fun ensureBuilder(action: GestureDescription.Builder.() -> Unit) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {if (_gestureBuilder == null) {_gestureBuilder = GestureDescription.Builder()}_gestureBuilder?.action()}}//<editor-fold desc="操作方法">fun moveDuration(startTime: Long = DEFAULT_GESTURE_START_TIME,duration: Long = DEFAULT_GESTURE_MOVE_DURATION) {this.startTime = startTimethis.duration = duration}fun flingDuration(startTime: Long = DEFAULT_GESTURE_START_TIME,duration: Long = DEFAULT_GESTURE_FLING_DURATION) {this.startTime = startTimethis.duration = duration}fun doubleDuration(startTime: Long = DEFAULT_GESTURE_START_TIME,duration: Long = DEFAULT_GESTURE_CLICK_DURATION,doubleDuration: Long = DEFAULT_GESTURE_DOUBLE_DURATION) {this.startTime = startTimethis.duration = durationthis.doubleDuration = doubleDuration}/**点击*/fun touch(fromX: Float, fromY: Float,startTime: Long = this.startTime,duration: Long = this.clickDuration) {touch(PointF(fromX, fromY), startTime, duration)}/**点击*/fun touch(fromX: Int, fromY: Int) {touch(Point(fromX, fromY))}/**点击*/fun touch(point: PointF,startTime: Long = this.startTime,duration: Long = this.clickDuration) {touch(Path().apply {moveTo(point.x, point.y)}, startTime, duration)}/**点击*/fun touch(point: Point) {touch(PointF(point))}/**移动*/fun touch(fromX: Float,fromY: Float,toX: Float,toY: Float,startTime: Long = this.startTime,duration: Long = this.clickDuration) {touch(Path().apply { moveTo(fromX, fromY);lineTo(toX, toY) }, startTime, duration)}/**移动*/fun touch(fromX: Int, fromY: Int, toX: Int, toY: Int) {touch(fromX.toFloat(), fromY.toFloat(), toX.toFloat(), toY.toFloat())}/**双击*/fun double(fromX: Float, fromY: Float) {double(PointF(fromX, fromY))}/**双击*/fun double(fromX: Int, fromY: Int) {double(fromX.toFloat(), fromY.toFloat())}/**双击*/fun double(point: PointF) {//双击, 需要伴随MOVE事件, 才能生效touch(point.x,point.y,point.x - nextInt(5, 10),point.y + nextInt(5, 10))startTime += duration + doubleDurationtouch(point.x,point.y,point.x + nextInt(5, 10),point.y - nextInt(5, 10))}/**手势操作核心*/fun touch(path: Path,startTime: Long = this.startTime,duration: Long = this.duration,willContinue: Boolean = this.willContinue) {if (_isDo) {L.w("$path ignore touch stroke.".apply {//AutoParseInterceptor.log(this)})return}ensureBuilder {try {//AutoParseInterceptor.log("添加手势:$startTime ms,$duration ms")if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {addStroke(GestureDescription.StrokeDescription(path,startTime,duration,willContinue))} else {addStroke(GestureDescription.StrokeDescription(path,startTime,duration))}} catch (e: Exception) {e.printStackTrace()}}}//</editor-fold desc="操作方法">
}/**DSL*/
fun AccessibilityService.dslGesture(action: DslAccessibilityGesture.() -> Unit = {}): Boolean {val gesture = DslAccessibilityGesture().apply {service = this@dslGestureaction()doIt()}return gesture._isDispatched
}fun AccessibilityService.gesture(): DslAccessibilityGesture? =if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {DslAccessibilityGesture().apply {service = this@gesture}} else {null}//<editor-fold desc="move">fun DslAccessibilityGesture.move(fromX: Int,fromY: Int,toX: Int,toY: Int,startTime: Long = this.startTime,duration: Long = DslAccessibilityGesture.DEFAULT_GESTURE_MOVE_DURATION,result: GestureResult? = null
): Boolean {return touch(fromX.toFloat(),fromY.toFloat(),toX.toFloat(),toY.toFloat(),startTime,duration,result)
}fun DslAccessibilityGesture.move(fromX: Float,fromY: Float,toX: Float,toY: Float,startTime: Long = this.startTime,duration: Long = DslAccessibilityGesture.DEFAULT_GESTURE_MOVE_DURATION,result: GestureResult? = null
): Boolean {moveDuration()return touch(fromX, fromY, toX, toY, startTime, duration, result = result)
}fun DslAccessibilityGesture.moveUp(result: GestureResult? = null): Boolean {val screenWidth = _screenWidthval screenHeight = _screenHeightval fX = screenWidth / 2 * 1f + nextInt(5, 10)val fY = screenHeight * 3 / 5 * 1f - nextInt(5, 10)val tY = screenHeight * 2 / 5 * 1f + nextInt(5, 10)return move(fX, fY, fX, tY, result = result)
}fun DslAccessibilityGesture.moveDown(result: GestureResult? = null): Boolean {val screenWidth = _screenWidthval screenHeight = _screenHeightval fX = screenWidth / 2 * 1f + nextInt(5, 10)val fY = screenHeight * 3 / 5 * 1f - nextInt(5, 10)val tY = screenHeight * 2 / 5 * 1f + nextInt(5, 10)return move(fX, tY, fX, fY, result = result)
}fun DslAccessibilityGesture.moveLeft(result: GestureResult? = null): Boolean {val screenWidth = _screenWidthval screenHeight = _screenHeightval fY = screenHeight / 2 * 1f + nextInt(5, 10)val fX = screenWidth * 3 / 5 * 1f + nextInt(5, 10)val tX = screenWidth * 2 / 5 * 1f - nextInt(5, 10)return move(fX, fY, tX, fY, result = result)
}fun DslAccessibilityGesture.moveRight(result: GestureResult? = null): Boolean {val screenWidth = _screenWidthval screenHeight = _screenHeightval fY = screenHeight / 2 * 1f + nextInt(5, 10)val fX = screenWidth * 3 / 5 * 1f + nextInt(5, 10)val tX = screenWidth * 2 / 5 * 1f - nextInt(5, 10)return move(tX, fY, fX, fY, result = result)
}//</editor-fold desc="move">//<editor-fold desc="fling">fun DslAccessibilityGesture.fling(fromX: Float,fromY: Float,toX: Float,toY: Float,startTime: Long = DslAccessibilityGesture.DEFAULT_GESTURE_START_TIME,duration: Long = DslAccessibilityGesture.DEFAULT_GESTURE_FLING_DURATION,result: GestureResult? = null
): Boolean {flingDuration(startTime, duration)return touch(fromX, fromY, toX, toY, startTime, duration, result)
}/**手指往上[fling] ↑*/
fun DslAccessibilityGesture.flingUp(result: GestureResult? = null): Boolean {val screenWidth = _screenWidthval screenHeight = _screenHeightval fX = screenWidth / 2 * 1f + nextInt(5, 10)val fY = screenHeight * 3 / 5 * 1f - nextInt(5, 10)val tY = screenHeight * 2 / 5 * 1f + nextInt(5, 10)return fling(fX, fY, fX, tY, result = result)
}/**手指往下[fling] ↓*/
fun DslAccessibilityGesture.flingDown(result: GestureResult? = null): Boolean {val screenWidth = _screenWidthval screenHeight = _screenHeightval fX = screenWidth / 2 * 1f + nextInt(5, 10)val fY = screenHeight * 3 / 5 * 1f - nextInt(5, 10)val tY = screenHeight * 2 / 5 * 1f + nextInt(5, 10)return fling(fX, tY, fX, fY, result = result)
}fun DslAccessibilityGesture.flingLeft(result: GestureResult? = null): Boolean {val screenWidth = _screenWidthval screenHeight = _screenHeightval fY = screenHeight / 2 * 1f + nextInt(5, 10)val fX = screenWidth * 3 / 5 * 1f + nextInt(5, 10)val tX = screenWidth * 2 / 5 * 1f - nextInt(5, 10)return fling(fX, fY, tX, fY, result = result)
}fun DslAccessibilityGesture.flingRight(result: GestureResult? = null): Boolean {val screenWidth = _screenWidthval screenHeight = _screenHeightval fY = screenHeight / 2 * 1f + nextInt(5, 10)val fX = screenWidth * 3 / 5 * 1f + nextInt(5, 10)val tX = screenWidth * 2 / 5 * 1f - nextInt(5, 10)return fling(tX, fY, fX, fY, result = result)
}//</editor-fold desc="fling">//<editor-fold desc="other">fun DslAccessibilityGesture.touch(fromX: Float,fromY: Float,toX: Float,toY: Float,startTime: Long = this.startTime,duration: Long = this.clickDuration,result: GestureResult? = null
): Boolean {gestureResult = resulttouch(fromX, fromY, toX, toY, startTime, duration)return doIt()
}fun DslAccessibilityGesture.click(x: Float = _screenWidth / 2f,y: Float = _screenHeight / 2f,startTime: Long = this.startTime,duration: Long = this.clickDuration,result: GestureResult? = null
): Boolean {gestureResult = resulttouch(x, y, startTime, duration)return doIt()
}fun DslAccessibilityGesture.double(x: Float = _screenWidth / 2f,y: Float = _screenHeight / 2f,startTime: Long = DslAccessibilityGesture.DEFAULT_GESTURE_START_TIME,duration: Long = DslAccessibilityGesture.DEFAULT_GESTURE_DOUBLE_DURATION,result: GestureResult? = null
): Boolean {gestureResult = resultdoubleDuration(startTime, duration)double(x, y)return doIt()
}/**随机在屏幕中产生一个点位信息*/
fun randomPoint(offsetLeft: Int = 10 * dpi,offsetTop: Int = _satusBarHeight,offsetRight: Int = 10 * dpi,offsetBottom: Int = _navBarHeight
): Point {val screenWidth = _screenWidthval screenHeight = _screenHeightval x: Int = nextInt(offsetLeft, screenWidth - offsetRight)val y: Int = nextInt(offsetTop, screenHeight - offsetBottom)return Point(x, y)
}fun nextDp(from: Int, until: Int) = nextInt(from * dpi, until * dpi)//</editor-fold desc="other">/**随机操作, 返回随机操作名称*/
fun DslAccessibilityGesture.randomization(): Pair<Boolean, String> {val p1 = PointF(randomPoint())val p2 = PointF(randomPoint())return when (nextInt(10)) {0 -> fling(p1.x, p1.y, p2.x, p2.y) to "fling ${p1}->${p2}"1 -> move(p1.x, p1.y, p2.x, p2.y) to "move ${p1}->${p2}"2 -> click(p1.x, p1.y) to "click $p1"3 -> double(p1.x, p1.y, result = null) to "double $p1"4 -> fling(p1.x, p1.y, p2.x, p2.y) to "fling ${p1}->${p2}"5 -> fling(p1.x, p1.y, p2.x, p2.y) to "fling ${p1}->${p2}"6 -> fling(p1.x, p1.y, p2.x, p2.y) to "fling ${p1}->${p2}"7 -> fling(p1.x, p1.y, p2.x, p2.y) to "fling ${p1}->${p2}"8 -> click(p1.x, p1.y) to "click $p1"9 -> double(p1.x, p1.y, result = null) to "double $p1"else -> true to "pass"}
}

有了这个工具类, 可以很快速的执行常规的手势操作, 如下:

click 点击

DslAccessibilityGesture.click(x,y)

double 双击

DslAccessibilityGesture.double(x,y)

move 移动

DslAccessibilityGesture.move(x1,y1, x2,y2)

fling 快速移动

DslAccessibilityGesture.fling(x1,y1, x2,y2)

系列文章

  • acc–›Android无障碍开发入门
  • acc–›Android无障碍开发常用操作
  • acc–›Android无障碍开发手势操作
  • acc–›Android无障碍开发框架

联系作者

群内有各(pian)种(ni)各(jin)样(qun)的大佬,等你来撩.

点此QQ对话 该死的空格 点此快速加群

acc--›Android无障碍开发手势操作相关推荐

  1. 【Android 应用开发】Android 无障碍开发简介 ( Android 无障碍开发辅助技术 | 启用 TalkBack 无障碍功能 | TalkBack 无障碍开发 示例 )

    文章目录 一.Android 无障碍开发辅助技术 二.启用 TalkBack 无障碍功能 三.TalkBack 无障碍功能代码示例 官方文档 : Android 无障碍功能概览 一.Android 无 ...

  2. android手机上的ancs,Android BLE开发之操作IOS ANCS

    前言 之前写过两篇有关于ANCS的文章,最近一段时间老是有人问关于得到ANCS服务的问题,因为IOS ANCS不同于其他的Peripheral一样对周边所有的蓝牙设备广播自己,而是仅有连接上配对并连接 ...

  3. Android 触摸及手势操作GestureDetector

    现在的智能手机不敢说百分百的都是触摸屏,也应该是百分之九九以上为触摸屏了,触摸屏为我们操作无键盘.无鼠标的手机系统带来了很多的便利.当用户触摸屏幕时会产生很多的触摸事件,down.up.move等等. ...

  4. mix2 android 8,小米MIX2升级Android 8.0 手势操作将一同推送

    小米最初以MIUI起家,其可玩程度极高也成就了"为发烧而生"的精神.MIUI实为基于Android定制的手机系统,但其个性化体验.功能丰富程度和易用实用度远超原生Android,除 ...

  5. 抖音Android无障碍开发知识总结

    抖音无障碍背景 国家近期开展了无障碍建设活动.为了积极响应国家号召,为抖音视障用户能够得到更好的交互体验,对抖音无障碍功能进行了专项治理和改造. 无障碍模式下的使用方法 抖音的无障碍功能实现主要是通过 ...

  6. Android无障碍开发(AccessibilityService)入门

    一.AccessibilityService 根据官方的介绍,是指开发者通过增加类似contentDescription的属性,从而在不修改代码的情况下,让残障人士能够获得使用体验的优化,大家可以打开 ...

  7. Android无障碍开发

    Android开发 GT框架导入 服务中打开其它应用 UI线程操作 GT框架导入 参考文档:https://blog.csdn.net/qq_39799899/article/details/1196 ...

  8. 【安卓开发 】Android初级开发(网络操作)

    URI部分 URI详情 uri的具体案例使用参考,app与网页之间的页面跳转 H5唤醒app并跳转到指定页面 H5打开APP技术总结 H5页面唤醒app的方法 Android配置Scheme使用浏览器 ...

  9. acc--›Android无障碍开发入门

    文章目录 前言 创建无障碍程序 1.配置无障碍信息 属性的说明 accessibilityEventTypes accessibilityFeedbackType accessibilityFlags ...

最新文章

  1. 抽象类在ASP.NET的学习与应用
  2. vscode安装本地服务器_如何用本地的VSCode连接极链AI的GPU服务器
  3. 面试题-两个数值交换
  4. S3C2440、S3C2450和S3C6410之间区别
  5. 由于这台计算机没有终端服务器客户端访问许可证,远程会话被中断解决办法...
  6. linux的bash脚本
  7. js的concat函数、join 、slice函数及二维数组的定义方式
  8. Pandas读取excel中的数据,并利用现有列数据生成新列
  9. RocketMQ之消费者顺序消费源码解析
  10. 手机浏览器哪家强,这3款口碑极佳的浏览器值得一用
  11. 牛逼!这个C++跳棋游戏居然可以让你边玩游戏边学编程!
  12. VSCode中出现未定义标识符,可以找到引用但是依旧标红
  13. Android卡顿优化
  14. python前端——HTML超文本标记语言、CSS层叠样式表
  15. 数据赋能:Uber的数据治理实践分享
  16. 网上超火的微信昵称和头像创意玩法 个性又帅气 有意思!
  17. Win7系统访问局域网-取消需要密码的方法
  18. 电脑版和手机版QQ都要手机版QQ扫描二维码登录?
  19. LPG-PCA算法实现与详解
  20. Oanda外汇账户截至20141201净值403

热门文章

  1. 电源系列4:一文学会选择BUCK降压电路电感
  2. 我和CSDN的故事(CDSN成立20周年———准程序员响应号召)
  3. 水雨情监测系统 实时监测
  4. Corel VideoStudio会声会影2020免费版+汉化包+序列号PC端电脑下载
  5. 统计学入门基础概念问答(统计学方法的分类、统计数据的分类等)
  6. MEMS电容式加速度传感器(简介)-传感器专题
  7. fluent p1模型_Fluent辐射传热模型理论以及相关设置(一)
  8. gunicorn + Flask架构中使用多进程全局锁
  9. VS CODE下 无法执行 node-gyp 命令
  10. “隐私—数据收集”问题的两难分析