Mobile
Article

Android Gestures and Touch Mechanics

By Valdio Veliu

I think we all agree that mobile technology has become a big part of the technology era and is now one of the main focuses of developers. One of the reasons for the success of mobile is the ability to interact with users through gestures and accessibility features. Mobile technology has set new limits to the term User Interaction.

Users interact with smartphones in a variety of different way,s from simple touch mechanics to dragging views and multi-finger gestures. Here is an example of the complete set of primary Mobile Gestures.

Mobile Gestures

In this article I will introduce some of the most important Android user interactions and gestures and implement custom Android gestures and touch events.

To make a fully interactive app a developer needs more than just onClick so the Android SDK supports a variety of gestures to detect. These allow developers to design the way users interact with an applications. Android provides the GestureDetector class to detect basic motion events and MotionEvent for handling more complicated gestures.

1. GestureDetector

The GestureDetector class receives motion events that correspond to a specific set of user gestures. Such as tapping down and up, swiping vertically and horizontally (fling), long and short press, double taps and scrolls. GestureDetector is powerful for these standard user interactions and is easy to set SimpleOnGestureListeners on Android UI elements.

The simplicity of this class lays in overriding the methods you need from the GestureListener and implementing the gesture functionality required without having to manually determine what gesture the user performed.

Detecting Common Gestures

The following example is an example of the common gestures of the GestureDetector class. First, declare a GestureDetector instance inside your Activity class.

  private GestureDetector mGestureDetector;

Create a java class inside the Activity class that implements the GestureDetector.OnGestureListener and GestureDetector.OnDoubleTapListener interfaces to detect the basic Android gestures.

  class Android_Gesture_Detector implements GestureDetector.OnGestureListener,
            GestureDetector.OnDoubleTapListener {

       @Override
        public boolean onDown(MotionEvent e) {
            Log.d("Gesture ", " onDown");
            return true;
        }

    @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            Log.d("Gesture ", " onSingleTapConfirmed");
            return true;
        }

    @Override
        public boolean onSingleTapUp(MotionEvent e) {
            Log.d("Gesture ", " onSingleTapUp");
            return true;
        }

        @Override
        public void onShowPress(MotionEvent e) {
            Log.d("Gesture ", " onShowPress");
        }

    @Override
        public boolean onDoubleTap(MotionEvent e) {
            Log.d("Gesture ", " onDoubleTap");
         return true;
        }

        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
            Log.d("Gesture ", " onDoubleTapEvent");
            return true;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            Log.d("Gesture ", " onLongPress");
        }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

            Log.d("Gesture ", " onScroll");
            if (e1.getY() < e2.getY()){
                Log.d("Gesture ", " Scroll Down");
            }
          if(e1.getY() > e2.getY()){
                Log.d("Gesture ", " Scroll Up");
            }
            return true;
    }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    if (e1.getX() < e2.getX()) {
              Log.d("Gesture ", "Left to Right swipe: "+ e1.getX() + " - " + e2.getX());
               Log.d("Speed ", String.valueOf(velocityX) + " pixels/second");
     }
            if (e1.getX() > e2.getX()) {
              Log.d("Gesture ", "Right to Left swipe: "+ e1.getX() + " - " + e2.getX());
   Log.d("Speed ", String.valueOf(velocityX) + " pixels/second");
            }
            if (e1.getY() < e2.getY()) {
            Log.d("Gesture ", "Up to Down swipe: " + e1.getX() + " - " + e2.getX());
              Log.d("Speed ", String.valueOf(velocityY) + " pixels/second");
            }
            if (e1.getY() > e2.getY()) {
              Log.d("Gesture ", "Down to Up swipe: " + e1.getX() + " - " + e2.getX());
   Log.d("Speed ", String.valueOf(velocityY) + " pixels/second");
            }
            return true;

       }
}

The Android_Gesture_Detector class will intercept all the basic gestures implemented in it, but to use these gestures I override the Activity’s onTouchEvent() method to intercept these touch events and re-route them to the Android_Gesture_Detector class.

  @Override
public boolean onTouchEvent(MotionEvent event) {
    mGestureDetector.onTouchEvent(event);

    return super.onTouchEvent(event);
    // Return true if you have consumed the event, false if you haven't.
    // The default implementation always returns false.
}

Let’s fire up the gesture detector. In the onCreate() method add the following and the Android gesture detector is ready to detect user interactions.

  // Create an object of the Android_Gesture_Detector  Class
Android_Gesture_Detector  android_gesture_detector  =  new Android_Gesture_Detector();
// Create a GestureDetector
mGestureDetector = new GestureDetector(this, android_gesture_detector);

Testing the gesture class

Run the app and start interacting with the app. The result of the gestures will display in the Android Studio LogCat window.

Test Output

Once detected you can build the app functionality inside the overridden methods.

Pinch Gesture

Another useful gesture in any app is the ability to scale UI elements. Android provides the ScaleGestureDetector class to handle pinch gestures for scaling views. The following code is an implementation of a simple Android app that uses the ScaleListener class to perform the pinch gesture on an imageView and scale it based on finger movements.

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private ImageView imageView;
    private float scale = 1f;
    private ScaleGestureDetector detector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView=(ImageView)findViewById(R.id.imageView);
        detector = new ScaleGestureDetector(this,new ScaleListener());
    }

public boolean onTouchEvent(MotionEvent event) {
//  re-route the Touch Events to the ScaleListener class
        detector.onTouchEvent(event);
        return super.onTouchEvent(event);
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {


        float onScaleBegin = 0;
        float onScaleEnd = 0;

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            scale *= detector.getScaleFactor();
            imageView.setScaleX(scale);
            imageView.setScaleY(scale);
            return true;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {

            Toast.makeText(getApplicationContext(),"Scale Begin" ,Toast.LENGTH_SHORT).show();
            onScaleBegin = scale;

            return true;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {

            Toast.makeText(getApplicationContext(),"Scale Ended",Toast.LENGTH_SHORT).show();
            onScaleEnd = scale;

            if (onScaleEnd > onScaleBegin){
                Toast.makeText(getApplicationContext(),"Scaled Up by a factor of  " + String.valueOf( onScaleEnd / onScaleBegin ), Toast.LENGTH_SHORT  ).show();
            }

            if (onScaleEnd < onScaleBegin){
                Toast.makeText(getApplicationContext(),"Scaled Down by a factor of  " + String.valueOf( onScaleBegin / onScaleEnd ), Toast.LENGTH_SHORT  ).show();
            }

            super.onScaleEnd(detector);
        }
      }
}

The ScaleListener class overrides three methods onScale, onScaleBegin and onScaleEnd. During the execution of the onScale method, the resizing of the imageView is performed based on the scale factor of the finger movement on the device screen.

Pinch Gesture

2. Motion Event

The Android Simple GestureDetector is useful for basic gestures. But for any gesture looking for two (or more) touches, you need to look elsewhere. To implement custom and more complex touch mechanics we use Motion Event. In most applications you don’t need to get your hands dirty implementing your own touch event but if you ever want to make a fully interactive app with cool touch events this class is the solution. The core of the MotionEvent class is capturing touch events on an Activity, by overriding the onTouchEvent() callback or on Views by using the setOnTouchListener() method to listen for touch events on the view. View touch events can be caught by implementing View.OnTouchListener on the Activity class as I will in the following example.

Android generates the following touch events whenever the screen is touched by one or multiple fingers.

ACTION_DOWN

For the first pointer that touches the screen. New touch started.

ACTION_MOVE

A change has happened in the touch gesture. Finger is moving.

ACTION_UP

The last pointer leaves the screen.

ACTION_POINTER_DOWN

For extra pointers that enter the screen beyond the first. (multi-touch)

ACTION_POINTER_UP

Sent when a non-primary pointer goes up. Pointer up (multi-touch)

ACTION_CANCEL

The touch event has been canceled, something else took control of the event.

To show the use of the MotionEvent class let’s think of an example to use it in a real life application. The reason gestures exist are to interact with the application’s UI so I will introduce a simple example of app that moves views around the main layout on a one finger gesture, stretches the view on a two finger gesture and spins it around on a three finger gesture. If you think about it, these gestures probably will never be used together on a real app, but as this is for demonstrative purposes I will implement them together inside a touch event.

Let’s gets started with these touch events. Create an Android Activity that implements View.OnTouchListener. In the activity’s layout file set the main Relative layout an id of rootLayout. As implemented in the example, the views on this activity will be able to respond to the touch events triggered by the users actions on this layout.

The idea is to add imageView elements on the root Layout, each with the ability to act based on a touch event. To add new imageViews on the layout we add a button that calls the Add_Image() function every time it’s clicked.

This function creates an imageView, sets it a image resource, creates a set of layout parameters, pins them to the imageView, adds this imageView to the root Layout and sets the Touch Listener to this view.

    private void Add_Image() {
        final ImageView iv = new ImageView(this);
        iv.setImageResource(R.drawable.image);

        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 150);

        iv.setLayoutParams(layoutParams);
        RootLayout.addView(iv, layoutParams);
        iv.setOnTouchListener(this);
    }

The most important part is the view TouchListener. The listener performs actions based on the touch events described above.

During the ACTION_DOWN event I get the touch’s X and Y position and remove the view’s respective margins to get the precise point inside the view in which the touch was performed.

During ACTION_UP event I implemented a custom double click event to delete the view on which this event is performed.

During ACTION_MOVE I implemented actions for the cool features described above. One cool thing about the MotionEvent class is the ability to detect the number of fingers that have touched the screen with the getPointerCount() method.

One finger – move the view.

Get the LayoutParams of the view and reset them based on the finger movement on the screen. The left margin is made equal to the position of the touch event minus the X value of the touch inside the view set during ACTION_DOWN so the view will move based on the X value of the finger on the Layout and relatively align itself based on the position of the touch inside the view.

The same logic applies to the top margin, but based on the Y axis. The right and bottom margins are set to 500 so the view doesn’t shrink when it touches the right and bottom borders of the layout, but hides itself behind it.

RelativeLayout.LayoutParams Params = (RelativeLayout.LayoutParams) view.getLayoutParams();
                        Params.leftMargin = X - Position_X;
                        Params.topMargin = Y - Position_Y;
                        Params.rightMargin = -500;
                        Params.bottomMargin = -500;
                        view.setLayoutParams(Params);

Two fingers – stretch the view

Get the LayoutParams of the view and resize the width and height of the view. During this resize action the view margins don’t change and the resizing will change based on the movement of the first finger so the sizes will be set to be the values of the first finger’s position on the layout. The constants Possition_X and Possition_Y do not influence the resizing, they are for visual purposes.

 RelativeLayout.LayoutParams layoutParams1 =  (RelativeLayout.LayoutParams) view.getLayoutParams();
                            layoutParams1.width = Position_X +(int)event.getX();
                            layoutParams1.height = Position_Y + (int)event.getY();
                            view.setLayoutParams(layoutParams1);

Three fingers – spin the view around

The spinning of the view is quite simple. Get the view’s current rotation, add a float constant to it and set it again to the view with setRotation() method during a three finger ACTION_MOVE.

view.setRotation(view.getRotation() + 10.0f);

Keep in mind that the resizing of the view will not work properly after the rotation event because the view’s axis are rotated to its current rotation state.

The full code on the MotionEvent Activity is now:

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;


public class MainActivity extends Activity implements View.OnTouchListener  {
    int clickCount;
    private ViewGroup RootLayout;
    private int Position_X;
    private int Position_Y;

    long startTime = 0 ;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RootLayout = (ViewGroup) findViewById(R.id.rootLayout);


        //new image
        Button NewImage = (Button)findViewById(R.id.new_image_button);
        NewImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Add_Image();
            }
        });

        clickCount = 0;

    }


    private void Add_Image() {
        final ImageView iv = new ImageView(this);
        iv.setImageResource(R.drawable.image);

        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 150);
        iv.setLayoutParams(layoutParams);
        RootLayout.addView(iv, layoutParams);
        iv.setOnTouchListener(this);
    }


    public boolean onTouch(final View view, MotionEvent event) {
        final int X = (int) event.getRawX();
        final int Y = (int) event.getRawY();

        int pointerCount = event.getPointerCount();


            switch (event.getAction() & MotionEvent.ACTION_MASK) {

                case MotionEvent.ACTION_DOWN:
                    RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
                    Position_X = X - layoutParams.leftMargin;
                    Position_Y = Y - layoutParams.topMargin;
                    break;

                case MotionEvent.ACTION_UP:
                    if (startTime == 0){

                        startTime = System.currentTimeMillis();

                    }else {
                        if (System.currentTimeMillis() - startTime < 200) {

                            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                            builder.setMessage("Are you sure you want to delete this?");
                            builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {

                                    view.setVisibility(View.GONE);

                                }
                            });

                            builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.dismiss();
                                }
                            });

                            AlertDialog alertDialog = builder.create();
                            alertDialog.show();

                        }

                        startTime = System.currentTimeMillis();

                    }
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    break;

    case MotionEvent.ACTION_POINTER_UP:
                    break;

                case MotionEvent.ACTION_MOVE:

                    if (pointerCount == 1){
                        RelativeLayout.LayoutParams Params = (RelativeLayout.LayoutParams) view.getLayoutParams();
                        Params.leftMargin = X - Position_X;
                        Params.topMargin = Y - Position_Y;
                        Params.rightMargin = -500;
                        Params.bottomMargin = -500;
                        view.setLayoutParams(Params);
                    }

                    if (pointerCount == 2){

                            RelativeLayout.LayoutParams layoutParams1 =  (RelativeLayout.LayoutParams) view.getLayoutParams();
                            layoutParams1.width = Position_X +(int)event.getX();
                            layoutParams1.height = Position_Y + (int)event.getY();
                            view.setLayoutParams(layoutParams1);
                    }

                    //Rotation
                    if (pointerCount == 3){
                        //Rotate the ImageView
                        view.setRotation(view.getRotation() + 10.0f);
                    }

                    break;
            }

// Schedules a repaint for the root Layout.
        RootLayout.invalidate();
        return true;
    }
}

Testing the MotionEvent app

Move views

Move Views

Resize view

Resize Views

Spin view

Spin View

Conclusion

In this article I presented the two main types of Android gesture classes with the basic knowledge to get you started using them. Use what you have learned here to improve your app’s user experience and let me know if you have any questions or comments.

  • http://careersreport.com Earlm Hardin

    There is an awesome idea how U can earn 85 dollars h… After being without work for 6 months , I started working over this internet-site and today I am verry happy. After 3 months on my new job my income is around $5000month -Check internet-website check out my profile

  • http://www.mobileapptelligence.com/ kailash mobileapptelligence

    In mobile application development process, nine-patch is the process of resizing the image. Like 4 cornet that are unscaled, 4 edges that are scaled in 1 axis and the middle one that has the capability of scaling into both axes. Mobileapptelligence [dot] com, an award winning Android app development company, is delivering best in class Android apps to global clients.

  • http://SalaryNet30.com Autumn Baker

    May` I tell you something`that is Really` interesting` and `worth` paying` `attention`. An `effective` and `excellent` online` `opportunity` to work` for those people` who want to `utilise` their free time so that they can `Earn some `extra `Money` using their `computers`… I have been `working`time so that they can `Earn some `extra `Money` using their `computers`… I have been `working` on this for last two and half years and I am earning` 60-90 `dollar`/ hour` … In the `Past` `Week` I` Have `Earned` 13,70 `DoLLars` For `Almost `20` hours` `Sitting` ….

    `Any` Special“kind` of `Skills, `Degree` or Specific qualification is not `required` for this, just `typing` and a `good` `working` and `reliable` `internet` `connection` ….

    `Any` `Time` `Boundations` to `Start work` is not `Required` … You may do this `work` at any `time` when you `Willing` to do it ….

    I have Been Working on this and Getting Results…..….Hope over to“website“ `page` `LINK` which is on Prrof!le of mine

    %55555555

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in Mobile, once a week, for free.