GestureDescription.StrokeDescription, отменить willContinue
Ситуация такая. На экране телефона отображается список. Программа должна листать его снизу вверх от нижней границы экрана до верхней. Но если использовать вот такую функцию
fun swipeScreen(swipePath: Path, startTime: Long = 0L, duration: Long = 1L): Boolean {
val gesture: GestureDescription = GestureDescription.Builder().apply {
addStroke(GestureDescription.StrokeDescription(swipePath, startTime, duration))
}.build()
return dispatchResult = dispatchGesture(gesture, null, null)
}
, то произойдет свайп, и список пролистается из начала сразу в конец. Поэтому в конце свайпа программа должна задержаться и "отпустить" жест через некоторое время, чтобы список пролистался примерно на 1 экран. Частично проблема решается, если указать 4 параметр GestureDescription.StrokeDescription (willContinue = true)
fun swipeScreen(swipePath: Path, startTime: Long = 0L, duration: Long = 1L): Boolean {
val gesture: GestureDescription = GestureDescription.Builder().apply {
addStroke(GestureDescription.StrokeDescription(swipePath, startTime, duration, true))
}.build()
return dispatchResult = dispatchGesture(gesture, null, null)
}
Но в описании к этому параметру написано следующее: "Continued strokes keep their pointers down when the gesture completes". Каким образом прекратить удерживание? Или, возможно, есть вообще другой вариант?
UPD. Я решил этот вопрос. Нашел как раз таки другой вариант.
Ответы (1 шт):
Скажу сразу. Вариант только лишь с использованием параметра willContinue мне не подошел, т.к. я так и не нашел, как программно "отпустить" нажатие. А мне это было нужно. Я пытался выполнить программный тап по экрану в точке окончания свайпа. Тогда этот новый жест (тап) прерывал предыдущий (свайп с задержкой от willContinue) и нажатие отпускалось. Но тап - это тап, который мог попасть, например, в гиперссылку в тексте, спровоцировав переход.
Поэтому я пошел следующим путем. Эту функцию
fun swipeScreen(swipePath: Path, startTime: Long = 0L, duration: Long = 1L): Boolean {
val gesture: GestureDescription = GestureDescription.Builder().apply {
addStroke(GestureDescription.StrokeDescription(swipePath, startTime, duration))
}.build()
return dispatchResult = dispatchGesture(gesture, null, null)
}
пришлось сильно изменить. Для начала я добавил входной параметр endDelay
fun swipeScreenWithDelay(
swipePath: Path,
startTime: Long = 0L,
duration: Long = 1L,
endDelay: Long = 500L
): Boolean
По умолчанию он равен 500 миллисекундам, но я использую около 300, когда вызываю функцию. Далее я разбил путь свайпа swipePath на 2 отрезка: первый = 99,5% от swipePath, второй = 0,5% от swipePath.
val pM = PathMeasure(swipePath, false)
val breakPoint: Float = pM.length * 0.995F
val p1 = Path()
pM.getSegment(0F, breakPoint, p1, true)
val p2 = Path()
pM.getSegment(breakPoint, pM.length, p2, true)
Создал 2 жеста для каждого из двух отрезков пути
val gesture1 = GestureDescription.StrokeDescription(p1, startTime, duration, true)
val gesture2 = gesture1.continueStroke(p2, 0, endDelay, false)
Тут сделаю небольшую ремарку:
- Первоначальный путь разбивается на 2 отрезка таким образом, чтобы конец первого и начало второго совпадали. Это важно. Если первый отрезок будет, допустим, первые 30%, а второй - последние 40%, то выполнится движение только по первому отрезку. В догонку: тем способом, которым я воспользовался, концы не совсем совпадают из-за типа Float, разница в десятитысячных, но есть. Однако ввиду того, что свайп все-таки выполняется на экране по пикселям с целочисленными координатами, то после округления, концы совпадают.
- Обратите внимание на то, что первый жест таки имеет параметр willContinue = true. Это нужно, чтобы второй жест был корректно создан через метод continueStroke первого жеста. То есть тут мы обозначаем, что второй жест - это именно продолжение первого жеста. И второй жест выполняется сразу без задержек после первого. Тут я пробовал не использовать continueStroke, а просто создать жест, подобно первому. Итог: не работает. Если нужна цепочка действий, то continueStroke в помощь.
Итак. Пришло время отправить жест на исполнение.
val dispatchResult = dispatchGesture(
GestureDescription.Builder().addStroke(gesture1).build(),
object : GestureResultCallback() {
override fun onCompleted(gestureDescription: GestureDescription) {
super.onCompleted(gestureDescription)
dispatchGesture(
GestureDescription.Builder().addStroke(gesture2).build(),
null,
null
)
}
override fun onCancelled(gestureDescription: GestureDescription) {
super.onCancelled(gestureDescription)
}
},
null
)
На самом деле, по-моему, не очень прозрачно. Тут мы перегружаем callback для первого жеста и "говорим", чтобы второй жест выполнился, когда первый будет успешно завершен. А если будет 3 и более жестов, продолжающих друг-друга? В таком случае я бы не делал такую матрешку, а написал бы отдельный метод для вызова цепочки жестов. Возможно, что и так напишу, попозже. Тогда выложу тут.
Все целиком.
fun swipeScreenWithDelay(
swipePath: Path,
startTime: Long = 0L,
duration: Long = 1L,
endDelay: Long = 500L
): Boolean {
/** Получить данные [swipePath] */
val pM = PathMeasure(swipePath, false)
/** Разбить [swipePath] на 2 отрезка: первый - 99,5% от [swipePath], второй - 0,5%. */
val breakPoint: Float = pM.length * 0.995F
val p1 = Path()
pM.getSegment(0F, breakPoint, p1, true)
val p2 = Path()
pM.getSegment(breakPoint, pM.length, p2, true)
/** Создать 2 жеста. Второй - продолжает первый через continueStroke. */
val gesture1 = GestureDescription.StrokeDescription(p1, startTime, duration, true)
val gesture2 = gesture1.continueStroke(p2, 0, endDelay, false)
/**
* Отправить жест на выполнение. Одновременно перегрузить callback таким образом, чтобы
* второй жест выполнился сразу, как только первый сообщит об успешном завершении.
*/
val dispatchResult = dispatchGesture(
GestureDescription.Builder().addStroke(gesture1).build(),
object : GestureResultCallback() {
override fun onCompleted(gestureDescription: GestureDescription) {
super.onCompleted(gestureDescription)
dispatchGesture(
GestureDescription.Builder().addStroke(gesture2).build(),
null,
null
)
}
override fun onCancelled(gestureDescription: GestureDescription) {
super.onCancelled(gestureDescription)
}
},
null
)
return dispatchResult
}
Итого. Когда выполняется свайп через приведенную функцию, в конце пути происходит резкое замедление, из-за которого экран не пролистывается. В принципе, это похоже на пролистывание экрана человеком.