acc--›Android无障碍开发手势操作
生活随笔
收集整理的這篇文章主要介紹了
acc--›Android无障碍开发手势操作
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
文章目錄
- 前言
- dispatchGesture `api>=24`
- GestureDescription
- GestureResultCallback
- 執(zhí)行手勢
- DslAccessibilityGesture
- click 點(diǎn)擊
- double 雙擊
- move 移動
- fling 快速移動
- 系列文章
- 聯(lián)系作者
前言
無障礙可以執(zhí)行觸屏手勢操作,并且非常簡單.
函數(shù)簽名如下api>=24:
android.accessibilityservice.AccessibilityService#dispatchGesture官方文檔地址
dispatchGesture api>=24
GestureDescription
構(gòu)建手勢:
- 聲明一個構(gòu)造器
- 創(chuàng)建一個手勢
- 添加手勢
GestureResultCallback
手勢執(zhí)行回調(diào).
val gestureResultCallback = object : AccessibilityService.GestureResultCallback() {override fun onCancelled(gestureDescription: GestureDescription?) {super.onCancelled(gestureDescription)//手勢取消}override fun onCompleted(gestureDescription: GestureDescription?) {super.onCompleted(gestureDescription)//手勢完成} }執(zhí)行手勢
dispatchGesture方法必須在主線程調(diào)用.
如果未指定Handler, gestureResultCallback默認(rèn)在主線程回調(diào).
如果上一個手勢還未執(zhí)行完成, 下一個手勢就觸發(fā)了, 則上一個手勢會被中斷.
如果用戶干預(yù)了手勢執(zhí)行, 手勢也會被中斷.
如果在主線程執(zhí)行手勢, 那么主線程卡頓時, 也會影響手勢執(zhí)行的結(jié)果.
service.dispatchGesture(gestureBuilder.build(),gestureResultCallback,null)DslAccessibilityGesture
這里有一份我封裝的手勢操作類, kotlin語言編寫.
typealias GestureResult = (gestureDescription: GestureDescription? /*執(zhí)行的手勢*/, dispatched: Boolean /*是否發(fā)送*/, canceled: Boolean /*是否被取消*/) -> Unit@TargetApi(Build.VERSION_CODES.N) class DslAccessibilityGesture {companion object {//開始時間const val DEFAULT_GESTURE_START_TIME = 16L//點(diǎn)擊時長const val DEFAULT_GESTURE_CLICK_DURATION = 16L//雙擊間隔時長const val DEFAULT_GESTURE_DOUBLE_DURATION = 60L//如果Duration時間太短, 將會產(chǎn)生flingconst val DEFAULT_GESTURE_MOVE_DURATION = 600Lconst val DEFAULT_GESTURE_FLING_DURATION = 30L //值太大, 將沒有fling效果}/**執(zhí)行回調(diào)*/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/**無障礙服務(wù), 用于執(zhí)行手勢*/var service: AccessibilityService? = null/*** 用于構(gòu)建手勢, 支持多指觸控* [android.accessibilityservice.GestureDescription.getMaxStrokeCount]* */var _gestureBuilder: GestureDescription.Builder? = nullvar _gestureResultCallback: AccessibilityService.GestureResultCallback? = null//是否發(fā)送了事件var _isDispatched: Boolean = false/**是否執(zhí)行完成*/var _isCompleted: Boolean = falsevar _countDownLatch: CountDownLatch? = null//是否已經(jīng)有手勢在執(zhí)行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}/**開始執(zhí)行手勢*/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) {//設(shè)備支持手勢_isDo = truereturn if (isMain()) {_isDispatched = service.dispatchGesture(builder.build(),_gestureResultCallback,null)L.w("派發(fā)手勢:$_isDispatched")_isDispatched} else {MainExecutor.execute {_isDispatched = service.dispatchGesture(builder.build(),_gestureResultCallback,null)L.w("派發(fā)手勢:$_isDispatched")}_countDownLatch = CountDownLatch(1)_countDownLatch?.await()_isCompleted}//AutoParseInterceptor.log("派發(fā)手勢:$_isDispatched")} else {//設(shè)備不支持手勢gestureResult?.invoke(null, false, true)//AutoParseInterceptor.log("設(shè)備不支持手勢")L.w("設(shè)備不支持手勢")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}/**點(diǎn)擊*/fun touch(fromX: Float, fromY: Float,startTime: Long = this.startTime,duration: Long = this.clickDuration) {touch(PointF(fromX, fromY), startTime, duration)}/**點(diǎn)擊*/fun touch(fromX: Int, fromY: Int) {touch(Point(fromX, fromY))}/**點(diǎn)擊*/fun touch(point: PointF,startTime: Long = this.startTime,duration: Long = this.clickDuration) {touch(Path().apply {moveTo(point.x, point.y)}, startTime, duration)}/**點(diǎn)擊*/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() }/**隨機(jī)在屏幕中產(chǎn)生一個點(diǎn)位信息*/ 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">/**隨機(jī)操作, 返回隨機(jī)操作名稱*/ 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"} }有了這個工具類, 可以很快速的執(zhí)行常規(guī)的手勢操作, 如下:
click 點(diǎn)擊
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無障礙開發(fā)入門
- acc–?Android無障礙開發(fā)常用操作
- acc–?Android無障礙開發(fā)手勢操作
- acc–?Android無障礙開發(fā)框架
聯(lián)系作者
群內(nèi)有各(pian)種(ni)各(jin)樣(qun)的大佬,等你來撩.
點(diǎn)此QQ對話 該死的空格 點(diǎn)此快速加群
總結(jié)
以上是生活随笔為你收集整理的acc--›Android无障碍开发手势操作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用emqx做mqtt客户端
- 下一篇: android sina oauth2.