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

相关推荐
Libraeking1 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位1 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
JMchen1233 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs4 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob4 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔4 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像
hudawei9964 小时前
flutter和Android动画的对比
android·flutter·动画
lxysbly6 小时前
md模拟器安卓版带金手指2026
android
儿歌八万首6 小时前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节
消失的旧时光-19439 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed