1

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
}
Shatas
  • 106
  • 7

0 Answers0