Rereading the Art of Android Development, this is a summary of chapter 3.

The contents of this chapter include

View basics, event distribution, sliding, and how to resolve sliding conflicts.

See mind maps for more details

Some code related stuff

To obtainTouchSlop

ViewConfiguration.get(this).scaledTouchSlop
Copy the code

To obtainVelocityTracker

// Tracks the speed of the current click event
val velocityTracker = VelocityTracker.obtain()
velocityTracker.addMovement(event)
/ / calculate first
velocityTracker.computeCurrentVelocity(1000)
// Get the current speed
val xVelocity = velocityTracker.xVelocity
val yVelocity = velocityTracker.yVelocity

...

// Do not use, need to reset
velocityTracker.clear()
velocityTracker.recycle()
Copy the code

useGestureDetector

Create a GestureDetector object
val mGestureDetector = GestureDetector(context, object : GestureDetector.OnGestureListener {
    override fun onDown(e: MotionEvent?).: Boolean {
        // The moment of touch
        TODO("Not yet implemented")}override fun onShowPress(e: MotionEvent?). {
        // Touch the screen without releasing or dragging
        TODO("Not yet implemented")}override fun onSingleTapUp(e: MotionEvent?).: Boolean {
        // Release, click behavior
        TODO("Not yet implemented")}override fun onScroll(
        e1: MotionEvent? , e2:MotionEvent? , distanceX:Float,
        distanceY: Float.): Boolean {
        // Finger press and drag, drag behavior
        TODO("Not yet implemented")}override fun onLongPress(e: MotionEvent?). {
        / / long press
        TODO("Not yet implemented")}override fun onFling(
        e1: MotionEvent? , e2:MotionEvent? , velocityX:Float,
        velocityY: Float.): Boolean {
        // Press, slide quickly and then release, fast sliding behavior
        TODO("Not yet implemented")}})// Set the double-click listener
mGestureDetector.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {
    override fun onSingleTapConfirmed(e: MotionEvent?).: Boolean {
        // The strict click behavior is a click in a click rather than a double click
        TODO("Not yet implemented")}override fun onDoubleTap(e: MotionEvent?).: Boolean {
        // Double click
        TODO("Not yet implemented")}override fun onDoubleTapEvent(e: MotionEvent?).: Boolean {
        // Send double click behavior, will be called multiple times
        TODO("Not yet implemented")}})// Solve the problem that the screen cannot be dragged after long pressing
mGestureDetector.setIsLongpressEnabled(false)
// Pass in the event of the listened view
val consume = mGestureDetector.onTouchEvent(event)

Copy the code

useScroller

// Implement elastic sliding
private val mScroller = Scroller(context)
private fun smoothScrollTo(destX: Int, destY: Int) {
    val scrollX = scrollX
    val delta = destX - scrollX
    // 1000ms inner slide to destX,
    mScroller.startScroll(scrollX, 0, delta, 0.1000)
    invalidate()
}

override fun computeScroll(a) {
    super.computeScroll()
    if (mScroller.computeScrollOffset()) {
        scrollTo(mScroller.currX, mScroller.currY)
        postInvalidate()
    }
}
Copy the code

The main code that resolves sliding collisions using external interception only needs to modify the parent control

// Add the following code to the parent control, mainly to control whether to intercept events, interception add according to the actual situation
// External interception
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
    var intercept = false
    val x = event.x.toInt()
    val y = event.y.toInt()
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            intercept = false
            if(! mScroller.isFinished) { mScroller.abortAnimation() intercept =true
            }
        }
        MotionEvent.ACTION_MOVE -> {
            val deltaX = x - mLastXIntercept
            val deltaY = y - mLastYIntercept
            // The current control needs to intercept the judgment of the current event, where the condition is: horizontal slide is greater than vertical slide to intercept
            intercept = abs(deltaX) > abs(deltaY)
        }
        MotionEvent.ACTION_UP -> {
            intercept = false
        }
    }
    mLastX = x
    mLastY = y
    mLastXIntercept = x
    mLastYIntercept = y
    return intercept
}
Copy the code

Use internal interception to resolve sliding conflicts in the main code parent control code

// Internal interception
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
    val x = event.x.toInt()
    val y = event.y.toInt()
    val action = event.action
    // The ACTION_DOWN event cannot be intercepted. Once intercepted, all events cannot be passed to child controls
    if (action == MotionEvent.ACTION_DOWN) {
        mLastX = x
        mLastY = y
        if(! mScroller.isFinished) { mScroller.abortAnimation()return true
        }
        return false
    } else {
        return true}}Copy the code

Child control code

// Internal interception
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
    val x = event.x.toInt()
    val y = event.y.toInt()
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            // mParentView needs to be passed to the current control
            mParentView.requestDisallowInterceptTouchEvent(true)
        }
        MotionEvent.ACTION_MOVE -> {
            val deltaX = x - mLastX
            val deltaY = y - mLastY
            // The condition that the parent control needs to intercept the event
            if (abs(deltaX) > abs(deltaY)) {
                mParentView.requestDisallowInterceptTouchEvent(false)
            }
        }
        MotionEvent.ACTION_UP -> {

        }
    }
    mLastX = x
    mLastY = y
    return super.dispatchTouchEvent(event)
}
Copy the code