acc--›Android无障碍开发手势操作
生活随笔
收集整理的這篇文章主要介紹了
acc--›Android无障碍开发手势操作
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 前言
- dispatchGesture `api>=24`
- GestureDescription
- GestureResultCallback
- 執行手勢
- DslAccessibilityGesture
- click 點擊
- double 雙擊
- move 移動
- fling 快速移動
- 系列文章
- 聯系作者
前言
無障礙可以執行觸屏手勢操作,并且非常簡單.
函數簽名如下api>=24:
android.accessibilityservice.AccessibilityService#dispatchGesture官方文檔地址
dispatchGesture api>=24
GestureDescription
構建手勢:
- 聲明一個構造器
- 創建一個手勢
- 添加手勢
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无障碍开发手势操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用emqx做mqtt客户端
- 下一篇: NBUT 1119 Patchouli'