重读《Android 开发艺术探索》,本篇是书中的第三章内容的总结。

本章内容包括

View 基础、事件分发、滑动及了解了前面的知识如何解决滑动冲突android是什么系统问题。

更多细节见思维导图

View 事件体系 -- 重读《Android 开发艺术探索》

View 事件体系 -- 重读《Android 开发艺术探索》

View 事件体系 -- 重读《Android 开发艺术探索》

View 事件体系 -- 重读《Android 开发艺术探索》

View 事件体系 -- 重读《Android 开发艺术探索》

View 事件体系 -- 重读《Android 开发艺术探索》

一些代码相关的内容

获取 Tandroid/yunosouchSlop

ViewConfiguration.get(this).scaledTouchSlop

获取 VelocityTracker

// 追踪当前点击事件的速度
val velocityTracker = VelocityTracker.obtain()
velocityTracker.addMovement(event)
// 先计算
velocityTracker.computeCurrentVelocity(1000)
// 获得当前速度
val xVelocity = velocityTracker.xVelocity
val yVelocity = velocityTracker.yVelocity
...
// 不用使用,需要重置
velocityTracker.clear()
velocityTracker.recycle()

使用 GestureDetector

// 创建 GestureDetector 对象
val mGestureDetector = GestureDetector(context, object : GestureDetector.OnGestureListener {
    override fun onDown(e: MotionEvent?): Boolean {
        //  触摸的一瞬间
        TODO("Not yet implemented")
    }
    override fun onShowPress(e: MotionEvent?) {
        // 触摸屏幕,未松开或拖动
        TODO("Not yet implemented")
    }
    override fun onSingleTapUp(e: MotionEvent?): Boolean {
        // 松开,点击行为
        TODO("Not yet implemented")
    }
    override fun onScroll(
        e1: MotionEvent?,
        e2: MotionEvent?,
        distanceX: Float,
        distanceY: Float,
    ): Boolean {
        // 手指按下并拖动,拖动行为
        TODO("Not yet implemented")
    }
    override fun onLongPress(e: MotionEvent?) {
        // 长按
        TODO("Not yet implemented")
    }
    override fun onFling(
        e1: MotionEvent?,
        e2: MotionEvent?,
        velocityX: Float,
        velocityY: Float,
    ): Boolean {
        // 按下,快速滑动后松开,快速滑动行为
        TODO("Not yet implemented")
    }
})
// 设置 双击监听
mGestureDetector.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {
    override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
        // 严格的单击行为,是单击而非双击中的一次点击
        TODO("Not yet implemented")
    }
    override fun onDoubleTap(e: MotionEvent?): Boolean {
        // 双击,两次单击组成
        TODO("Not yet implemented")
    }
    override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
        // 发送双击行为,会被调用多次
        TODO("Not yet implemented")
    }
})
// 解决长按屏幕后无法拖动的现象
mGestureDetector.setIsLongpressEnabled(false)
// 传入被监听 view 的 event
val consume = mGestureDetector.onTouchEvent(event)

使用 Scrollandroid是什么手机牌子er

// 实现弹性滑动
private val mScroller = Scroller(context)
private fun smoothScrollTo(destX: Int, destY: Int) {
    val scrollX = scrollX
    val delta = destX - scrollX
    // 1000ms 内滑向 destX,
    mScroller.startScroll(scrollX, 0, delta, 0, 1000)
    invalidate()
}
override fun computeScroll() {
    super.computeScroll()
    if (mScroller.computeScrollOffset()) {
        scrollTo(mScroller.currX, mScroller.currY)
        postInvalidate()
    }
}

使用外部拦截法解决滑动冲突的主要代码 只需修改父控android/yunos

// 在父控件中添加以下代码,主要是控制是否要拦截事件,拦截添加根据实际情况添加
// 外部拦截法
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
            // 当前控件需要拦截当前事件的判断,此处的条件是:横行滑动大于垂直滑动时拦截
            intercept = abs(deltaX) > abs(deltaY)
        }
        MotionEvent.ACTION_UP -> {
            intercept = false
        }
    }
    mLastX = x
    mLastY = y
    mLastXIntercept = x
    mLastYIntercept = y
    return intercept
}

使用内部拦截法解决滑动冲突的主要代码 父控件代android是什么系统

// 内部拦截法
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
    val x = event.x.toInt()
    val y = event.y.toInt()
    val action = event.action
    // 不能拦截 ACTION_DOWN 事件,一旦拦截该事件那么所有事件都无法传递给子控件
    if (action == MotionEvent.ACTION_DOWN) {
        mLastX = x
        mLastY = y
        if (!mScroller.isFinished) {
            mScroller.abortAnimation()
            return true
        }
        return false
    } else {
        return true
    }
}

子控件代码

// 内部拦截法
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
    val x = event.x.toInt()
    val y = event.y.toInt()
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            // mParentView 需要被传递给当前控件
            mParentView.requestDisallowInterceptTouchEvent(true)
        }
        MotionEvent.ACTION_MOVE -> {
            val deltaX = x - mLastX
            val deltaY = y - mLastY
            // 父控件需要拦截该事件的条件
            if (abs(deltaX) > abs(deltaY)) {
                mParentView.requestDisallowInterceptTouchEvent(false)
            }
        }
        MotionEvent.ACTION_UP -> {
        }
    }
    mLastX = x
    mLastY = y
    return super.dispatchTouchEvent(event)
}