I have a circle as ImageView
and I want to be able to smoothly rotate it when swiping with a finger. If I swipe slowly, I want the circle to rotate slowly until the swiping action stops. If I swipe faster, I want the image to rotate more.
I've implemented the code that I've found here. This longer version allows to change swiping direction without lifting the finger, so that the circle would start rotating to the other direction.
This is my OnTouchListener
class:
open class OnSwipeTouchListenerV2() : View.OnTouchListener {
private val SWIPE_THRESHOLD = 0f
private var initialX = 0f
private var initialY = 0f
private var previousX = 0f
private var previousY = 0f
private var currentX = 0f
private var currentY = 0f
private var diffX = 0f
private var diffY = 0f
private var swipeH = "0" // Horizontal swipe direction (LEFT or RIGHT)
override fun onTouch(v: View, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
initialX = event.x
initialY = event.x
return true
}
MotionEvent.ACTION_MOVE -> {
currentX = event.x
currentY = event.x
// These where original diff calculations
// The issue with this is that diffs get bigger when swiping further
// It is an issue because I'm trying to use the diff as an angle to rotate by
// diffX = currentX - initialX // Original
// diffY = currentY - initialY // Original
diffX = currentX - previousX // My Implementation
diffY = currentY - previousY // My Implementation
when (swipeH) {
"LEFT" -> {
if (currentX > previousX) {
swipeH = "RIGHT"
initialX = previousX
diffX = currentX - initialX
} else {
// Intentionally kept empty
}
}
"RIGHT" -> {
if (currentX < previousX) {
swipeH = "LEFT"
initialX = previousX
diffX = currentX - initialX
} else {
// Intentionally kept empty
}
}
else -> {
if (currentX < initialX) {
swipeH = "LEFT"
} else if (currentX > initialX) {
swipeH = "RIGHT"
} else {
// Intentionally kept empty
}
}
}
previousX = currentX
previousY = currentY
if (abs(diffX) > abs(diffY)) {
if (abs(diffX) > SWIPE_THRESHOLD) {
if (diffX > 0) {
onSwipeRight(diffX)
} else {
onSwipeLeft(diffX)
}
}
} else {
if (abs(diffY) > SWIPE_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom(diffY)
} else {
onSwipeTop(diffY)
}
}
}
return true
}
MotionEvent.ACTION_UP -> {
swipeH = "0"
initialX = 0f
initialY = 0f
previousX = 0f
previousY = 0f
diffX = 0f
diffY = 0f
return true
}
else -> {
return false
}
}
}
open fun onSwipeRight(diffX: Float) {}
open fun onSwipeLeft(diffX: Float) {}
open fun onSwipeTop(diffY: Float) {}
open fun onSwipeBottom(diffY: Float) {}
}
This is my MainActivity
file:
class MainActivityBackup : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val wheel = findViewById<ImageView>(R.id.imageView1) as ImageView
window.decorView.setOnTouchListener(object: OnSwipeTouchListenerV2() {
override fun onSwipeLeft(diffX: Float) {
rotateImage(wheel, diffX)
}
override fun onSwipeRight(diffX: Float) {
rotateImage(wheel, diffX)
}
})
}
fun rotateImage(imageView: ImageView, angle: Float) {
imageView.animate().rotation(angle).start()
}
}
It kind of works, but not really... On emulator, if I swipe just a bit, then it works fine, but if I do a continuous swipe slowly, then the circle moves very little during the swipe and makes a normal rotation once the swipe is finished. Same happens during a fast longer swipe: the circle does not move during the swipe but rotates (more than during the slow swipe because I'm using diffX
as an angle of how much to rotate) once the swipe is finished.
I've tried a different function for the actual rotation found here. I've tried different interpolators, that did not help.
Maybe I should not be using diffX
for rotation? But from Log
messages (to get the value of diffX
during each ACTION_MOVE
) I saw that during a continuous swipe the circle definitely does not rotate as much as it is supposed to.
My guess is that this happens because during a continuous swipe the are a lot of MotionEvent.ACTION_MOVE
actions and each of them makes a call to onSwipeLeft
or onSwipeRight
functions, but they do not finish for some reason because a new MotionEvent.ACTION_MOVE
action arrives, right?
So, any ideas how to smoothly rotate a circle?
Solved
Figured it out myself... I guess it was because I was using animation and when there are many ACTION_MOVE
events happening, then animation just cannot keep up. This is the change that I've made (not exactly this, but it's the main idea why it was not working properly):
fun rotateImage(imageView: ImageView, angle: Float) {
// old code
imageView.animate().rotation(angle).start()
// new code
imageView.rotation = angle
}