Efthimis C.
Efthimis C.

Reputation: 11

Custom view with onTouch doesn't fling smoothly

I've made a simple class with gestures and I use it in a view.

I'm trying to do a simple swipe to dismiss effect but in the end, it's not that smooth. the translation is great but when the finger release happens, the velocity from the onFling function looks wrong..

class SwipeGestureListener : SimpleOnGestureListener, OnTouchListener {
    var context: Context? = null

    lateinit var flingDetector: GestureDetector

    lateinit var flingY: FlingAnimation
    lateinit var springBackTranslationYAnimation: SpringAnimation

    var lastY = 0f

    val minDistanceUp = 40
    val friction = 0.1f
    var minFlingArea = 0f
    var isItUpDirection = false

    constructor(
        context: Context?,
        dialog: View?
    ) {

        this.context = context

        dialog?.let {
            it.viewTreeObserver
                ?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
                    override fun onGlobalLayout() {
                        minFlingArea = -(it.height + it.top).toFloat() - 100f
                        it.viewTreeObserver.removeOnGlobalLayoutListener(this)
                    }
                })
        }

        flingDetector = GestureDetector(context, flingListener)

        springBackTranslationYAnimation = SpringAnimation(dialog,
            object : FloatPropertyCompat<View>("translationY") {
                override fun getValue(view: View): Float {
                    return view.translationY
                }

                override fun setValue(
                    view: View,
                    value: Float
                ) {
                    view.translationY = value
                }
            })

        val springForceY = SpringForce(0f)
        springForceY.stiffness = SpringForce.STIFFNESS_VERY_LOW
        springForceY.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
        springBackTranslationYAnimation.spring = springForceY
        springBackTranslationYAnimation.addUpdateListener(dynamicAnimationCallback())

        flingY = FlingAnimation(dialog, DynamicAnimation.TRANSLATION_Y)
    }

    private val flingListener: GestureDetector.OnGestureListener =
        object : SimpleOnGestureListener() {
            override fun onDown(e: MotionEvent?): Boolean {
                return true
            }

            override fun onFling(
                downEvent: MotionEvent,
                moveEvent: MotionEvent,
                velocityX: Float,
                velocityY: Float
            ): Boolean {

                val distanceY = downEvent.rawY - moveEvent.rawY

                if (distanceY >= minDistanceUp && isItUpDirection) {

                    flingY.setStartVelocity(if (velocityY > 0) -(velocityY) else velocityY)
                        .setMinValue(minFlingArea)
                        .setFriction(friction)
                        .start()
                } else {
                    springBackTranslationYAnimation.start()
                }

                return true
            }
        }

    override fun onTouch(view: View, event: MotionEvent): Boolean {
        view.performClick()

        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {

                flingY.cancel()
                springBackTranslationYAnimation.cancel()
            }
            MotionEvent.ACTION_MOVE -> {

                val deltaY = event.rawY - lastY

                view.translationY = deltaY + view.translationY

                isItUpDirection = event.rawY < lastY
            }
            MotionEvent.ACTION_UP -> {

                if (!isItUpDirection) {
                    springBackTranslationYAnimation.start()
                }
            }
        }

        lastY = event.rawY

        flingDetector.onTouchEvent(event)

        return true
    }
}

Any idea what's wrong here?

Thanks in advance!

Upvotes: 1

Views: 502

Answers (1)

Avinash
Avinash

Reputation: 897

I have implemented gesture for swipe left and swipe right.Please try this one.

import android.content.Context
import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener

open class OnSwipeTouchListener(ctx: Context) : OnTouchListener {

val gestureDetector: GestureDetector

companion object {

    private val SWIPE_THRESHOLD = 100
    private val SWIPE_VELOCITY_THRESHOLD = 100
}

init {
    gestureDetector = GestureDetector(ctx, GestureListener())
}

override fun onTouch(v: View, event: MotionEvent): Boolean {


    var isTouch = false

    if (gestureDetector != null && event != null) {

        isTouch = gestureDetector.onTouchEvent(event)
    } else {
        isTouch = true
    }

    return isTouch

}

inner class GestureListener : SimpleOnGestureListener() {

    override fun onDown(e: MotionEvent): Boolean {
        return false
    }

    override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
        var result = false
        try {


            val diffY = e1?.y?.let { e2?.y?.minus(it) }
            val diffX = e1?.x?.let { e2?.x?.minus(it) }

            if (diffX != null && diffY != null) {

                if (Math.abs(diffX) > Math.abs(diffY)) {
                    if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffX > 0) {
                            onSwipeRight()
                        } else {
                            onSwipeLeft()
                        }
                        result = true
                    }
                } else {

                }
            } else {
                onSwipeRight()

                result = true
            }


            /*else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
               if (diffY > 0) {
                   onSwipeBottom()
               } else {
                   onSwipeTop()
               }
               result = true
           }*/
        } catch (exception: Exception) {
            exception.printStackTrace()
        }

        return result
    }


}

open fun onSwipeRight() {}

open fun onSwipeLeft() {}

open fun onSwipeTop() {}

open fun onSwipeBottom() {}

open fun onSwipeDown() {

 }
}

Call from your view

     view?.setOnTouchListener(object : OnSwipeTouchListener(it) {

            override fun onTouch(v: View, event: MotionEvent): Boolean {


                return super.onTouch(v, event)
            }


            override fun onSwipeDown() {

                super.onSwipeDown()
            }

            override fun onSwipeRight() {


            }     
            override fun onSwipeLeft() {
                super.onSwipeLeft()


                        }

                    }
                }
            }
        }

Upvotes: 1

Related Questions