Android:实现一个全屏拖拽、自动贴边半隐藏的自定义View

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

今天给大家带来一个可全屏拖拽,手指离开屏幕后自动贴边,隔一定时间后自动半隐藏的这么一个效果。话不多说直接上效果图:

看到这个效果是不是感觉很熟悉?没错,很多商业APP首页都带一个小助手的图标,使用的时候点击它就自动弹出,不使用的时候自动贴边隐藏,当然也是可以随意全屏拖拽,为的是防止遮挡一些关键位置的信息,影响用户体验。接下来咱们就来一步步实现它!

要实现上图效果咱们得罗列所有的功能点:

  • 自定义View ,这里要显示图片所以继承自ImageView或其子类即可
  • 监听屏幕滑动事件,记录和计算当前视图的位置信息
  • 动画效果,很明显使用平移动画
  • 圆角图片和描边,使用第三方ImageView即可

为了解决小圆球这个图标的问题咱们自定View时直接继承自第三方RoundedImageView,一举两得直接解决了第一和第四步。咱们把焦点聚焦到第二三部分,这也是最为复杂的部分。

之前文章 Android:自定义View实现图片缩放及坐标的计算(上) 中有写到监听界面各类手势可以使用GestureDetector ,这里咱们就不采用重写onTouchEvent 方法然后再里面监听各类ACTION_UP、ACTION_DOWN、ACTION_MOVE事件的模式来写了。但还是需要重写onTouchEvent 方法将GestureDetector的处理结果返回给它即可:

kotlin 复制代码
override fun onTouchEvent(event: MotionEvent): Boolean {
    return gestureDetector.onTouchEvent(event)
}

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

首先需要在onDown 方法中记录最后点击屏幕的位置信息lastXlastY ,这里备份一份点击时的位置信息moveXmoveY,用于后续逻辑判断。

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

onScroll 中需要不停修改自定义视图的位置,所以我们需要计算出需要移动位置的信息。通过当前实时滑动点的信息和最后记录的点信息计算出滑动距离,再重新计算当前视图的上下左右位置,最后咱们采取layout() 方式进行位置设置。

scss 复制代码
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
}

到此,咱们已经实现了可全屏拖拽的效果了:

现在只差最后一步,通过位置信息判断图标该往哪边贴边,以及移动距离的计算。

由于GestureDetector 没有抬起监听,所以逻辑咱们还是得在onTouchEvent 方法中通过监听ACTION_UP的动作进行操作。判断该往哪边贴边很简单,如果最后松开的位置X坐标的超过屏幕一半就往右贴,反之往左。动画咱们还是使用ValueAnimator ,因为我们移动也是用layout() 方法进行操作。

kotlin 复制代码
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的所有内容,希望能给大家带来帮助!

相关推荐
烬奇小云2 小时前
认识一下Unicorn
android·python·安全·系统安全
顾北川_野14 小时前
Android 进入浏览器下载应用,下载的是bin文件无法安装,应为apk文件
android
CYRUS STUDIO14 小时前
Android 下内联汇编,Android Studio 汇编开发
android·汇编·arm开发·android studio·arm
右手吉他14 小时前
Android ANR分析总结
android
PenguinLetsGo16 小时前
关于 Android15 GKI2407R40 导致梆梆加固软件崩溃
android·linux
杨武博19 小时前
音频格式转换
android·音视频
音视频牛哥21 小时前
Android音视频直播低延迟探究之:WLAN低延迟模式
android·音视频·实时音视频·大牛直播sdk·rtsp播放器·rtmp播放器·android rtmp
ChangYan.21 小时前
CondaError: Run ‘conda init‘ before ‘conda activate‘解决办法
android·conda
二流小码农21 小时前
鸿蒙开发:ForEach中为什么键值生成函数很重要
android·ios·harmonyos
夏非夏1 天前
Android 生成并加载PDF文件
android