我们好,我是似曾相识2022。不喜欢唱跳篮球,但对杰伦的Rap却情有独钟。

今日给我们带来一个可全屏拖拽,手指离开屏暗地主动贴边,隔必定时间后主动半躲藏的这么一个效果。话不多说直接上效果图:

Android:完结一个全屏拖拽、主动贴边半躲藏的自定义View

看到这个效果是不是感觉很熟悉?没错,许多商业APP首页都带一个小辅佐的图标,运用的时分点击它就主动弹出,不运用的时分主动贴边躲藏,当然也是可以随意全屏拖拽,为的是避免遮挡一些要害方位的信息,影响用户体验。接下来我们就来一步步完结它!

要完结上图效果我们得罗列一切的功用点:

  • 自定义View,这儿要闪现图片所以继承自ImageView或其子类即可
  • 监听屏幕滑动工作,记载和核算其时视图的方位信息
  • 动画效果,很明显运用平移动画
  • 圆角图片和描边,运用第三方ImageView即可

为了处理小圆球这个图标的问题我们自定View时直接继承自第三方RoundedImageView,一箭双雕直接处理了第一和第四步。我们把焦点聚集到第二三部分,这也是最为杂乱的部分。

之前文章 Android:自定义View完结图片缩放及坐标的核算(上) 中有写到监听界面各类手势可以运用GestureDetector,这儿我们就不选用重写onTouchEvent方法然后再里面监听各类ACTION_UP、ACTION_DOWN、ACTION_MOVE工作的形式来写了。但仍是需求重写onTouchEvent方法将GestureDetector的处理结果回来给它即可:

override fun onTouchEvent(event: MotionEvent): Boolean {
    return gestureDetector.onTouchEvent(event)
}

接下来只需在GestureDetector入参的GestureDetector.SimpleOnGestureListener监听中履行对应的操作:

首要需求在onDown方法中记载终究点击屏幕的方位信息lastXlastY,这儿备份一份点击时的方位信息moveXmoveY,用于后续逻辑判别。

override fun onDown(e: MotionEvent): Boolean {
    lastX = e.rawX.toInt()
    lastY = e.rawY.toInt()
    moveX = lastX
    moveY = lastY
    return true
}

onScroll中需求不断批改自定义视图的方位,所以我们需求核算出需求移动方位的信息。通过其时实时滑动点的信息和终究记载的点信息核算出滑动间隔,再重新核算其时视图的上下左右方位,终究我们采纳layout() 方法进行方位设置。

override fun onScroll(
    e1: MotionEvent,
    e2: MotionEvent,
    distanceX: Float,
    distanceY: Float
): Boolean {
    //获取其时实时点信息
    val rawX = e2.rawX.toInt()
    val rawY = e2.rawY.toInt()
    //改动量
    dX = rawX - lastX
    dY = rawY - lastY
    //获取最新的视图方位
    var left = left + dX
    var right = right + dX
    var top = top + dY
    var bottom = bottom + dY
    //增加约束规模,上下左右不能超出屏幕规模
    if (left < 0) {
        left = 0
        right = left + width
    }
    if (right > windowWith) {
        right = windowWith
        left = right - width
    }
    if (top < 40) {
        top = 40
        bottom = top + height
    }
    if (bottom > windowHight) {
        bottom = windowHight
        top = bottom - height
    }
    //更新其时视图方位
    layout(left, top, right, bottom)
    //更新终究屏幕点信息
    lastX = rawX
    lastY = rawY
    return true
}

到此,我们现已完结了可全屏拖拽的效果了:

Android:完结一个全屏拖拽、主动贴边半躲藏的自定义View

现在只差终究一步,通过方位信息判别图标该往哪边贴边,以及移动间隔的核算。

因为GestureDetector没有抬起监听,所以逻辑我们仍是得在onTouchEvent方法中通过监听ACTION_UP的动作进行操作。判别该往哪边贴边很简单,假设终究松开的方位X坐标的超越屏幕一半就往右贴,反之往左。动画我们仍是运用ValueAnimator,因为我们移动也是用layout() 方法进行操作。

override fun onTouchEvent(event: MotionEvent): Boolean {
    when (event.action) {
        MotionEvent.ACTION_UP -> {
            val x = event.rawX
            val y = event.rawY
            //抬起点和终究一次按下点x、y间隔大于视图宽的一半才履行
            if (abs(x - moveX) > width / 2 || abs(y - moveY) > width / 2) {
                val isRight = x > windowWith / 2
                //贴边
                startAnimator(isRight, windowWith - width, 0)
                //隔1.5秒收边
                postDelayed({
                    startAnimator(isRight, windowWith - width * 2 / 3, -width / 3)
                }, 1500)
            }
            return true
        }
    }
    return gestureDetector.onTouchEvent(event)
}
//属性动画履行
private fun startAnimator(isRight: Boolean, rightValue: Int, leftValue: Int) {
        ValueAnimator.ofInt(
            left,
            if (isRight) rightValue else leftValue
        ).apply {
            addUpdateListener { animation ->
                val value = animation.animatedValue as Int
                //根据监听值不断改动其时视图方位
                layout(value, top, value + width, bottom)
            }
            //插值器  先快后慢
            interpolator = AccelerateDecelerateInterpolator()
            duration = 600
            start()
        }
    }

这儿运用了两次动画,第一次根据核算得出的方向进行贴边平移,隔了1.5秒后再进行躲藏的操作。到此我们的一切功用全部都完结了接下来总结几点:

  • 自定义View时尽量选择最接近方针功用的View进行继承
  • 屏幕工作监听除了重写onTouchEvent进行动作监听的方法还有GestureDetectorScaleGestureDetector等方法
  • 重写了onTouchEvent方法后需求注意其回来值,假设都回来false的情况该视图的点击工作有或许会被父View或其他设有监听工作控件所消费,导致滑动监听不被触发。

以上便是完结一个全屏拖拽、主动贴边半躲藏的自定义View的一切内容,希望能给我们带来帮助!