You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio中如何实现触摸时通过动画移动对象?

实现View跨布局移动的核心方案

我看过你提到的这个视频哦!它实现View(比如TextView)跨布局拖动并平滑移动到目标区域的核心方法,是结合触摸事件处理和属性动画(Property Animation),再配合视图坐标转换来完成的,具体拆解下来是这些关键步骤:

  • 拦截触摸事件,记录初始位置
    在目标View的onTouchEvent方法里,监听ACTION_DOWN事件,记录手指按下时的屏幕坐标、View当前的相对位置(getX()/getY())以及View自身的宽高。这一步是为后续计算移动偏移量做准备。

  • 实时跟随手指拖动View
    监听ACTION_MOVE事件,计算手指移动的偏移量,通过setX()/setY()方法实时更新View的位置,让它跟着手指滑动。如果遇到父布局拦截触摸事件的情况,还需要重写父布局的onInterceptTouchEvent方法,确保事件能传递到目标View。

  • 平滑动画过渡到目标布局
    当监听到ACTION_UP(手指松开)事件时,先调用目标布局的getLocationOnScreen()方法,把目标布局的相对坐标转换成屏幕绝对坐标。然后用ObjectAnimator或者更简洁的ViewPropertyAnimator创建属性动画,让View的x、y属性平滑过渡到目标位置,还可以搭配缩放、透明度动画让效果更自然。

  • 切换View的布局归属(动画收尾)
    动画结束后,记得把View从原布局中移除,添加到目标布局里,并调整它在目标布局中的LayoutParams,避免后续布局出现错乱。

核心代码示例(Kotlin)

// 给目标TextView设置触摸监听
textView.setOnTouchListener { view, event ->
    var initialX = 0f
    var initialY = 0f

    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            // 记录初始偏移量:View位置 - 手指屏幕坐标
            initialX = view.x - event.rawX
            initialY = view.y - event.rawY
            true
        }
        MotionEvent.ACTION_MOVE -> {
            // 实时更新View位置
            view.x = event.rawX + initialX
            view.y = event.rawY + initialY
            true
        }
        MotionEvent.ACTION_UP -> {
            // 获取目标布局的屏幕绝对坐标
            val targetLocation = IntArray(2)
            targetLayout.getLocationOnScreen(targetLocation)
            val targetX = targetLocation[0].toFloat()
            val targetY = targetLocation[1].toFloat()

            // 使用ViewPropertyAnimator执行平滑移动动画
            view.animate()
                .x(targetX)
                .y(targetY)
                .setDuration(300) // 动画时长300ms
                .setListener(object : Animator.AnimatorListener {
                    override fun onAnimationEnd(animation: Animator?) {
                        // 动画结束后切换View的父布局
                        (view.parent as ViewGroup).removeView(view)
                        targetLayout.addView(view)
                        // 重置View的布局参数
                        view.layoutParams = ViewGroup.LayoutParams(
                            ViewGroup.LayoutParams.WRAP_CONTENT,
                            ViewGroup.LayoutParams.WRAP_CONTENT
                        )
                    }

                    // 其他空实现方法
                    override fun onAnimationStart(animation: Animator?) {}
                    override fun onAnimationCancel(animation: Animator?) {}
                    override fun onAnimationRepeat(animation: Animator?) {}
                })
                .start()
            true
        }
        else -> false
    }
}

常见问题排查

如果你在实现时遇到卡顿或者坐标错误的问题,可以检查这几点:

  • 避免在ACTION_MOVE回调里做耗时操作,尽量保持逻辑轻量化
  • 区分相对坐标(getX())和绝对坐标(getLocationOnScreen()),不要混淆两种坐标体系
  • 如果View有复杂背景或阴影,动画前可以暂时禁用硬件加速(view.setLayerType(View.LAYER_TYPE_SOFTWARE, null)),避免动画卡顿

内容的提问来源于stack exchange,提问作者Renaldi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:41:44