Android
Touch Events
ACTION_MOVE
View Movement
Mobile Development

android move a view on touch move ACTION_MOVE

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Moving a view by dragging it in Android usually means listening for touch events and updating the view’s position during MotionEvent.ACTION_MOVE. The important part is not just reading finger coordinates, but preserving the offset between the finger and the view so the view does not “jump” when the drag starts. In practice, translationX and translationY are often the cleanest properties to update for this kind of direct manipulation.

Basic Drag Logic

The simplest drag interaction tracks the touch-down offset and repositions the view as the finger moves.

kotlin
1import android.annotation.SuppressLint
2import android.os.Bundle
3import android.view.MotionEvent
4import android.view.View
5import androidx.appcompat.app.AppCompatActivity
6
7class MainActivity : AppCompatActivity() {
8    private var dX = 0f
9    private var dY = 0f
10
11    @SuppressLint("ClickableViewAccessibility")
12    override fun onCreate(savedInstanceState: Bundle?) {
13        super.onCreate(savedInstanceState)
14        setContentView(R.layout.activity_main)
15
16        val draggable: View = findViewById(R.id.draggableView)
17
18        draggable.setOnTouchListener { view, event ->
19            when (event.actionMasked) {
20                MotionEvent.ACTION_DOWN -> {
21                    dX = view.x - event.rawX
22                    dY = view.y - event.rawY
23                    true
24                }
25                MotionEvent.ACTION_MOVE -> {
26                    view.animate()
27                        .x(event.rawX + dX)
28                        .y(event.rawY + dY)
29                        .setDuration(0)
30                        .start()
31                    true
32                }
33                else -> false
34            }
35        }
36    }
37}

This works because rawX and rawY are screen-based coordinates, while dX and dY preserve the initial difference between the view’s position and the finger position.

Why the Offset Matters

If you just set view.x = event.rawX, the view’s top-left corner snaps directly under the finger. That usually feels wrong because the drag begins from wherever the user touched inside the view, not necessarily from its top-left corner.

The offset calculation fixes that:

kotlin
dX = view.x - event.rawX
dY = view.y - event.rawY

Then every future move uses:

kotlin
view.x = event.rawX + dX
view.y = event.rawY + dY

That preserves the relative grab point naturally.

Prefer Translation for Some Cases

If the view is already laid out and you want a lightweight drag effect, changing translation can be simpler than updating layout parameters.

kotlin
1draggable.setOnTouchListener { view, event ->
2    when (event.actionMasked) {
3        MotionEvent.ACTION_DOWN -> {
4            dX = view.translationX - event.rawX
5            dY = view.translationY - event.rawY
6            true
7        }
8        MotionEvent.ACTION_MOVE -> {
9            view.translationX = event.rawX + dX
10            view.translationY = event.rawY + dY
11            true
12        }
13        else -> false
14    }
15}

This is often a good choice when the drag is purely visual and you do not need to permanently re-layout the view within the parent.

Keep the View Inside Its Parent

Real drag interactions usually need boundaries so the view cannot be dragged completely off screen.

kotlin
1val parent = draggable.parent as View
2
3val newX = (event.rawX + dX).coerceIn(0f, (parent.width - view.width).toFloat())
4val newY = (event.rawY + dY).coerceIn(0f, (parent.height - view.height).toFloat())
5
6view.x = newX
7view.y = newY

Without clamping, users can easily drag the view somewhere it cannot be reached again.

Parent Interception

If the draggable view lives inside a scrollable parent such as ScrollView or RecyclerView, the parent may intercept move events.

During the drag, request that the parent not intercept:

kotlin
view.parent.requestDisallowInterceptTouchEvent(true)

Call that on ACTION_DOWN and sometimes during move handling as well, depending on the gesture behavior you want.

Use performClick When Appropriate

If the view is normally clickable, accessibility tools expect click semantics. A pure drag listener can accidentally suppress them. If your interaction supports tap and drag, call performClick() on ACTION_UP when no meaningful movement happened.

This keeps the view more compatible with accessibility services and testing frameworks.

When to Use ViewDragHelper or Gestures

For simple free dragging, a touch listener is fine. For more structured drag behavior inside custom layouts, especially when multiple child views are involved, utilities such as ViewDragHelper can reduce edge-case handling and make gesture coordination cleaner.

If you only need swipe or fling recognition, a gesture detector may be a better abstraction than manual drag math.

Common Pitfalls

The biggest mistake is using raw touch coordinates without preserving the initial finger offset, which causes the view to jump. Another is forgetting that parent containers may intercept move events. Developers also often move the view outside the visible bounds because they never clamp the coordinates. Finally, using layout changes instead of simple translation for a purely visual drag can create unnecessary complexity and jank.

Summary

  • Handle ACTION_DOWN to capture the initial finger-to-view offset.
  • Update the view position during ACTION_MOVE using that saved offset.
  • Use translationX and translationY when a visual drag is enough.
  • Clamp coordinates if the view should stay inside its parent.
  • Watch out for parent touch interception in scrollable containers.

Course illustration
Course illustration

All Rights Reserved.