Motion Events with Drag and Drop in Android

Narendra Harny
Make Android
Published in
8 min readJul 31, 2021

This article covers important concepts about Motion Events with drag-drop of a view and related callbacks in Android.

Photo by Dimon Blr on Unsplash

The Android view system framework is a big topic to explain how internally works. Here in this article, I would like to cover the working example of dragging and dropping a view throughout the screen and the events callbacks of motion events.

First of all, we will examine at what is the significance of the motion events in Android.

Motion Event

To make it easy, when an Android device’s screen detects, the movement then the motion event callbacks occur, which execute a few callbacks in the form of various actions. It also delivers a set of axis values about the contact area of the device screen.

As reflected in the above image, when the touch event is acted the action_down event will be executed and when the touch event is released the action_up event will be executed It will also obtain the X and Y coordinates according to the contact area.

How Touch System Works

To Understand the touch system in Android, we have to understand three terms!!!

Activity Main View(decor View): The decor view is the main view of an activity that receives the touch input first and starts transferring the touch events to the respective view group.

View Groups: The View Group is a parent view group container of an XML associated with the activity. For example, LinearLayout, RelativeLayout

Views: The child views like buttons and text views contained by the parent view group in the activity XML file.

For example, we have an activity with a text view and the touch event is performed on the text view.

When the touch event is detected first it will be handled by Activity’s main view then it will be transferred to the View Group container and then will be transferred to the respective view.

As per the view hierarchy of nested view groups in xml file, there might be multiple event transfers in multiple view groups.

Callbacks on events transferred

The entry point for the touch, press, and click motion event is the Activity decor view which contains all the view groups and views for further transfer of the motion event.

It does the transfer of events from the parent view to the child view using dispatchTouchEvent.

The dispatchTouchEvent is a callback method that gets called on each event transfer from parent view to child view in the view tree, so going ahead from the activity’s decor view to down in the view tree, the dispatchTouchEvent will be called.

Touch Event Handeling

An important thing to know here is how the current view or view group knows that the touch event will be handled in its child view, and it needs to transfer the event to its child.

During the transfer of the touch event from the parent view to its child view, there is one more callback method that will be executed, called onInterceptTouchEvent.

Note that onInterceptTouchEvent() is a method from the ViewGroup class, and not from Activity.

onInterceptTouchEvent method returns a boolean value true or false in different case.

Ture: When the event is not transferred to its child view and it is handled in the current view or view group itself.
So when the interceptTouchEvent returns True, the event will not be dispatched down in the tree further.

False: When the touch event is dispatched one by one down in the view tree, onInterceptTouchEvent will keep returning false.

In this case, the touch event is not handled in the current view, and it needs to pass to its child view in the view tree.

The touch system has one more important callback contrast to onInterceptTouchEvent that is onTouchEvent which also returns the boolean value. onTouchEvent callback works right opposite to onInterceptTouchEvent.

If onTouchEvent returns true that means the touch is been handled with the current view and if it returns false then the Event will be transferred to its parent view and this happens from bottom to top in the view tree.

As mentioned above, the motion event delivers various actions like action_up and action_down.
The action codes are constants that are available in the MotionEvent class.

@Override
public boolean onTouch(View view, MotionEvent motionEvent) {}
// MotionEvent action constantsMotionEvent.ACTION_DOWN
MotionEvent.ACTION_UP
MotionEvent.ACTION_MOVE
...

After understanding the above concepts let’s do some sample codes to check the drag and drop feature and also check the use of MotionEvent Action codes.

Create an android Project and add the xml code to the main activity xml file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
>

<TextView
android:id="@+id/dragMe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Drag me"
android:layout_centerInParent="true"
/>
</RelativeLayout>

This activity has drag-me text which is used to perform the drag-and-drop functionality.

Step by step we will check the events and call back for Motion Events!!

public class MainActivity extends AppCompatActivity {

private static String TAG = MainActivity.class.getSimpleName();
private TextView dragMe;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dragMe.findViewById(R.id.dragMe);
dragMe.setOnTouchListener(new OnTouchEvent());
}

private class OnTouchEvent implements View.OnTouchListener {

@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.d(TAG, "onTouch: " + motionEvent.getAction());
return true;
}
}
}

This is the activity code where I added the onTouchListener to the dragme Text view.

Let’s run the code and look at the log!!!

You can perform multiple operations on the drag-me text and see the log cat here you will see the event logs with different codes delivered from the MotionEvent class.

If you see the code of MotionEvent class, you will found the meaning of these codes.

public final class MotionEvent extends InputEvent implements Parcelable {
public static final int ACTION_BUTTON_PRESS = 11;
public static final int ACTION_BUTTON_RELEASE = 12;
public static final int ACTION_CANCEL = 3;
public static final int ACTION_DOWN = 0;
public static final int ACTION_HOVER_ENTER = 9;
public static final int ACTION_HOVER_EXIT = 10;
public static final int ACTION_HOVER_MOVE = 7;
public static final int ACTION_MASK = 255;
public static final int ACTION_MOVE = 2;
public static final int ACTION_OUTSIDE = 4;
...
...

Now we will handle the action_down, action_down and anction_move events in the code.

private class OnTouchEvent implements View.OnTouchListener {

@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onTouch: MotionEvent.ACTION_DOWN" );
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "onTouch: MotionEvent.ACTION_UP" );
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onTouch: MotionEvent.ACTION_MOVE" );
break;
}
return true;
}
}

Let’s run the code again and look at the logcat!!!

Try to drag the text, you will see the call back in a sequence, action_down once initially, action_up once at last and action_move multiple times in between.

Now we will see how to drag the view!!!

As mentioned above the Motion Event class also delivers the X and Y axis for the view on which the event is performed. we can get the X and Y coordinates for the view using getX() and getY() methods.

So, in this case, the drag me text is the view that is handling the onTouchEvent, using the drag-me text we can get the X and Y coordinates.

case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onTouch: MotionEvent.ACTION_DOWN" );
Log.d(TAG, "onTouch: Initial DragMe X " + dragMe.getX());
Log.d(TAG, "onTouch: Initial DragMe Y" + dragMe.getY());

break;

This will give you the initial X and Y coordinates of drag-me text view.

To implement the drag-and-drop functionality, we will implement the below logic.

Here is the full Activity for the above explained logic!!!

public class MainActivity extends AppCompatActivity {

private static String TAG = MainActivity.class.getSimpleName();
private TextView dragMe;
float xDown = 0, yDown = 0;

@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dragMe = findViewById(R.id.dragMe);
dragMe.setOnTouchListener(new OnTouchEvent());
}

private class OnTouchEvent implements View.OnTouchListener {

@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onTouch: MotionEvent.ACTION_DOWN" );
Log.d(TAG, "onTouch: ACTION_DOWN X "
+ dragMe.getX());
Log.d(TAG, "onTouch: ACTION_DOWN Y "
+ dragMe.getY());
xDown = motionEvent.getX();
yDown = motionEvent.getY();

break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "onTouch: MotionEvent.ACTION_UP" );
Log.d(TAG, "onTouch: ACTION_UP X "
+ dragMe.getX());
Log.d(TAG, "onTouch: ACTION_UP Y "
+ dragMe.getY());
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onTouch: MotionEvent.ACTION_MOVE" );
float xMove, yMove;
xMove = motionEvent.getX();
yMove = motionEvent.getY();

float distX = xMove - xDown;
float distY = yMove - yDown;
Log.d(TAG, "onTouch: ACTION_MOVE distance X "
+ dragMe.getX());
Log.d(TAG, "onTouch: ACTION_MOVE distance Y "
+ dragMe.getY());

dragMe.setX(dragMe.getX() + distX);
dragMe.setY(dragMe.getY() + distY);
Log.d(TAG, "onTouch: ACTION_MOVE X "
+ dragMe.getX());
Log.d(TAG, "onTouch: ACTION_MOVE Y "
+ dragMe.getY());
break;
}
return true;
}
}
}

Now we will drag and drop the text once and we will analyze the callback action for the drag event.

Let’s understand the logs and call back action!!!

As mentioned above, the action_down will be called once initially, action_up will be called once at last, and action_move will be called multiple times in between.

Logs look like this!!!

As we are calculating the distance and final coordinates with action_move that’s why the last coordinates are similar for action_move and action_up.

I hope the drag-and-drop functionality is working smoothly, and that you enjoyed the article.

Here is the git link for a complete code example!!!

Happy Coding!!!

--

--

Narendra Harny
Make Android

Connect on https://medium.com/make-android | Write On, Android AOSP & Applications | Python | DevOps | Java | C++ | Kotlin | Shell | Linux | Android Auto | IVI