Android Studio中如何实现触摸时通过动画移动对象?
我看过你提到的这个视频哦!它实现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

