Interaction with the touch screen

Catching up on programming for Android need to know how to interact with a touchscreen - the main input device for most of the Android devices. This article will be considered: 
  • Low-level interaction with the touch screen, right above all for game development.
    • Class MotionEvent
    • Processing MotionEvent'ov
  • Working with MultiTouch.
  • Some high-touch processing techniques.
There will also be shown various examples of ...

The retreat over the Valley


Since later in this article, we will actively use the Log, I will briefly tell you about it (those who are already familiar with log Android may safely skip this section of the article).
In Android has its own log, which can write applications from all interested Dvalvik VM to your application. All messages in the log are separated by priority (right to choose the priority of the message is sent to the log is up to you)
To write to the log enough to cause one of the static class methods Log, differing only in priority sent by their messages.
These methods are listed below in ascending order of priority:
  • Log.v (String tag, String msg) - VERBOSE
  • Log.d (String tag, String msg) - DEBUG
  • Log.i (String tag, String msg) - INFO
  • Log.w (String tag, String msg) - WARNING
  • Log.e (String tag, String msg) - ERROR
  • Log.wtf (String tag, String msg) - What a Terrible Failure
It is also possible to send the log exceptions. In this case adds one more argument - Throwable tr. For example:
  • Log.v (String tag, String msg, Throwable tr)

Print the Log's window in Eclipse can be as follows: 
Window -> Show View -> Other -> Android -> LogCat

The concept of MotionEvent


MotionEvent class serves as a repository of data on a touch (touch event). Each time a user spends with his finger on the screen, or even just Tapa, created an entire sequence of instances MotionEvent: sequence starts when a user touches the screen, continues until the user moves a finger across the screen and ends when the user lifts his finger. Thus there are three main types of action that describes MotionEvent: lowering the finger, his movement and uplift. Each MotionEvent keeps information about the type of action, which he describes. To this end, the class provides constant MotionEvent:
  • int MotionEvent.ACTION_DOWN - user puts his finger on the screen.
  • int MotionEvent.ACTION_MOVE - the user moves a finger across the screen.
  • int MotionEvent.ACTION_UP - user lifts the finger from the screen.
Learn what effect this describes MotionEvent by using the method getAction ().
It is worth noting that there is another type of action - MotionEvent.ACTION_CANCEL, which is essentially a symbol of what the sequence is completed correctly, then there is no MotionEvent.ACTION_UP. All of this will be explained in more detail the example, but a little later.

Receiving and processing of MotionEvents



Now that we've got a general idea of ​​MotionEvent'ah must deal with the fact from us their "take." There are 2 ways, let us discuss them separately.
  • The first method is best when you use one of the standard View. In the View class has a method SetOnTouchListener (View.OnTouchListener l), an argument which is the class that implements the interface OnTouchListener. This method allows you to appoint a "listener" MotionEvent'ov. Interface OnTouchListener commits a class to implement the method public boolean OnTouch (View v, MotionEvent event), the second argument is that you want us to MotionEvent. Now every time the user will touch the screen, Android will call our method OnTouch, giving him a description of what happened in the argument of that is MotionEvent. Let us consider an example:
    Here's our file res / layout / main.xml:

    1 <? Xml version = "1.0" encoding = "utf-8"?>
    2 <LinearLayout xmlns: android = " schemas.android.com / apk / res / android "
    3 android: orientation = "vertical"
    4 android: layout_width = "fill_parent"
    5 android: layout_height = "fill_parent"
    6 android: id = "@ + id / ll"
    7>
    8 </ LinearLayout>

    But our class Activity:

    An import android.app.Activity;
    2 import android.os.Bundle;
    3 import android.util.Log;
    4 import android.view.MotionEvent;
    5 import android.view.View;
    6 import android.view.View.OnTouchListener;
    7 import android.widget.LinearLayout;
    8
    9 public class main extends Activity implements OnTouchListener {/ / makes our class to translate the interface Activity OnTouchListener
    10 / ** Called when the activity is first created. * /
    11 @ Override
    12 public void onCreate (Bundle savedInstanceState) {
    13 super. OnCreate (savedInstanceState);
    14 setContentView (R.layout.main);
    15 LinearLayout ll = (LinearLayout) this. FindViewById (R.id.ll); / / We get the desired View
    16 ll.setOnTouchListener (this); / / Set the class as a listener for our MotionEvent'ov LinearLayout
    17}
    18
    19 @ Override
    20 public boolean onTouch (View v, MotionEvent event) / / That's the method that will handle and MotionEvent'y.
    21 {
    22 int Action = event.getAction ();
    23 / / The method getAction () Gets the type of action (ACTION_DOWN, ACTION_MOVE or ACTION_UP)
    24 StringBuilder str = new StringBuilder ();
    25 str.append ("\ nActrion type:");
    26 / / Next for better (as a constant ACTION_DOWN, ACTION_MOVE ACTION_UP and numeric)
    27 / / hold switch on the variable Action, and add to our StringBuilder name of the constant
    28 switch (Action)
    29 {
    30 case MotionEvent.ACTION_DOWN: str.append («ACTION_DOWN \ n»); break;
    31 case MotionEvent.ACTION_MOVE: str.append («ACTION_MOVE \ n»); break;
    32 case MotionEvent.ACTION_UP: str.append («ACTION_UP \ n»); break;
    33}
    34 / / Use the methods getX () and getY () to obtain the coordinates x and y axes, respectively
    35 / / It should be noted that the point 0 is located in the upper left corner of the screen.
    36 / / x axis pointing to the right
    37 / / y axis pointing down (the lower, the greater the coordinate).
    38 str.append («Location:»). Append (event.getX ()). Append ("x"). Append (event.getY ()). Append ("\ n"); / / Learn coordinates
    39 str.append («Edge flags:»). Append (event.getEdgeFlags ()). Append ("\ n"); / / method returns information about getEdgeFlags crossing edges of the screen
    40 str.append («Pressure:»). Append (event.getPressure ()). Append ("\ n"); / / Learn pressure
    41 str.append («Size:»). Append (event.getSize ()). Append ("\ n"); / / Learn the pointer size (places a finger on the touch screen)
    42 str.append («Down time:»). Append (event.getDownTime ()). Append («ms \ n»); / / find out the time when the finger was lowered onto the screen in milliseconds
    43 str.append («Event time:»). Append (event.getEventTime ()). Append («ms»); / / check the current time (corresponding to the processed MotionEvent'u) in milliseconds
    44 str.append ("Elapsed:"). Append (event.getEventTime ()-event.getDownTime ()); / / find out how much time has passed since the lowering of a finger, to the current MotionEvent'a
    45 Log.v («Mytag», str.toString ()); / / In order to be able to monitor these activities, we write about them all the information in the log.
    46 return true; / / Why do we return true will be discussed later
    47}
    48}

    It is clear that one class can be a listener in several View. To consider this method takes an argument onTouch View. 
  • The second way is best, when you write your View class, which must respond to the touch. In View is a method onTouchEvent (MotionEvent event), which, actually, and can handle MotionEvent'y. Consider an example:
    Write the class SomeView:

    1 package com.example.MotionEventExample1;
    2 import android.content.Context;
    3 import android.util.AttributeSet;
    4 import android.util.Log;
    5 import android.view.MotionEvent;
    6 import android.view.View;
    7
    8 public class SomeView extends View
    9 {
    10 / / First, you must implement a constructor.
    11 / / It's simple - just call the superclass constructor
    12 public SomeView (Context context, AttributeSet attrs)
    13 {
    14 super (context, attrs);
    15}
    16
    17 / / Now proceed directly to processing MotionEvent'ov.
    18 / / You need to rewrite the method onTouchEvent
    19 @ Override
    20 public boolean onTouchEvent (MotionEvent event)
    21 {
    22
    23 Float X = (Float) event.getX ();
    24 Float Y = (Float) event.getY ();
    25 int Action = event.getAction ();
    26 String ActionString = "";
    27 switch (Action)
    28 {
    29 case MotionEvent.ACTION_DOWN: ActionString = «ACTION_DOWN»; break;
    30 case MotionEvent.ACTION_MOVE: ActionString = «ACTION_MOVE»; break;
    31 case MotionEvent.ACTION_UP: ActionString = «ACTION_UP»; break;
    32}
    33 Log.v («MyTag», «View OnTouchListener: \ n» + «Coords:» + X.toString () + "x" + Y.toString () + "\ nAction type:" + ActionString);
    34 return true;
    35}
    36
    37}
    38

    And our / res / layout / main.xml:

    1 <? Xml version = "1.0" encoding = "utf-8"?>
    2 <LinearLayout xmlns: android = " schemas.android.com / apk / res / android "
    3 android: orientation = "vertical"
    4 android: layout_width = "fill_parent"
    5 android: layout_height = "fill_parent"
    6>
    7 <com.example.MotionEventExample1.SomeView
    8 android: layout_width = "fill_parent"
    9 android: layout_height = "fill_parent"
    10 android: id = "@ + id / sv"
    11 />
    12 </ LinearLayout>
    13

    Activity class:
    1 package com.example.MotionEventExample1;
    An import android.app.Activity;
    2 import android.os.Bundle;
    3
    4 public class main extends Activity {
    5 / ** Called when the activity is first created. * /
    6 @ Override
    7 public void onCreate (Bundle savedInstanceState) {
    8 super. OnCreate (savedInstanceState);
    9 setContentView (R.layout.main);
    10}
    11}


The fact that the return handlers MotionEvent


As you can see, handlers must return MotionEvent's boolean value. In the examples before, we just always return true. So what do these methods must return a value? The fact that one View can be multiple listeners (eg onTouchEvent in the View and the outer OnTouch listener) and they have some precedence: OnTouch called first (if any), and after it can be called onTouchEvent. Call next on the priority of the handler just returned depends on the current value of the handler (true - there is nothing called, false - called the next priority, if any). Thus, Android allows us to share responsibility for handling touch on a few event'ov listeners.

Drag and Drop

So we figured out the basics of handling MotionEvento'ov. To consolidate the skills we write an application that implements a simple Drag and Drop.

Here is our / res / layout / main.xml:

1 <? Xml version = "1.0" encoding = "utf-8"?>
2 <LinearLayout xmlns: android = " schemas.android.com / apk / res / android "
3 android: orientation = "vertical"
4 android: layout_width = "fill_parent"
5 android: layout_height = "fill_parent"
6>
7 <com.example.dragdrop.SomeView
8 android: layout_width = "fill_parent"
9 android: layout_height = "fill_parent"
10 android: id = "@ + id / sv"
11 />
12 </ LinearLayout>

Here is our class Activity:

1 package com.example.dragdrop;
2
3 import android.app.Activity;
4 import android.os.Bundle;
5
6 public class main extends Activity {
7 / ** Called when the activity is first created. * /
8 @ Override
9 public void onCreate (Bundle savedInstanceState) {
10 super. OnCreate (savedInstanceState);
11 setContentView (R.layout.main);
12}
13}

But our main class SomeView:

1 package com.example.dragdrop;
2
3 import android.content.Context;
4 import android.graphics.Canvas;
5 import android.graphics.Color;
6 import android.graphics.Paint;
7 import android.graphics.Paint.Style;
8 import android.util.AttributeSet;
9 import android.view.MotionEvent;
10 import android.view.View;
11
12 public class SomeView extends View
13 {
14 Paint paint;
15 int X;
16 int Y;
17 final static int Radius = 20;
18 public SomeView (Context context, AttributeSet attrs)
19 {
20 super (context, attrs);
21 paint = new Paint ();
22 paint.setColor (Color.YELLOW);
23 paint.setStyle (Style.FILL);
24 X = 30;
25 Y = 30;
26}
27 @ Override
28 public boolean onTouchEvent (MotionEvent event)
29 {
30 X = (int) event.getX ();
31 Y = (int) event.getY ();
32 return true;
33}
34
35 @ Override
36 protected void onDraw (Canvas canvas) / / OnDraw method offered to Android when it needs to draw this View
37 {
38 canvas.drawCircle (X, Y, Radius, paint);
39 invalidate (); / / invalidate () is needed in order to alert the Android, you have to execute a method OnDraw again, without the View will not be perericovyvatsya.
40}
41
42}

Multitouch


In Android, how much would you finger-not used, information on all of them stored in one MotionEvent'e. The first method that you want to know if you intend to use multi - getPointerCount (), which returns the number of fingers, fixed on the screen (not always coincide with the actual number due to limitations in hardware). Each finger on the screen, given the index, and id. Indexes always start at 0, id - not necessarily. For clarity let us consider an example.
The first finger down - it is assigned index = 0 id = 0
The second finger down - it is assigned index = 1 and id = 1
The first finger raised - the second finger is assigned index = 0 (indices always start at zero), and id is the same
The second finger up - MotionEvent'ov no more, until the Next touch.
Id always reserved for a pointer (finger), so that we can always keep track of what you want the pointer.
When we have finished with the id and the indices, we consider how to get the properties (position, size, etc.) at a specific index, where many of them. To do this in class MotionEvent methods exist:
  • getX (int Index)
  • getY (int Index)
  • getSize (int Index)
and so on, we have considered them
In the argument of these methods take a pointer to the index.
But as the index pointer can vary, but often you want to track a specific finger, there is a very important method getPointerId (int index), which allows you to find the specified index id pointer.

Consider this example: rewrite our application (Drag and Drop) so that it viewed the multi-touch.
(Note: In multi-emulator does not verify, it can only be done on this devayse)
Here is our res / layout / main.xml
1 <? Xml version = "1.0" encoding = "utf-8"?>
2 <LinearLayout xmlns: android = " schemas.android.com / apk / res / android "
3 android: orientation = "vertical"
4 android: layout_width = "fill_parent"
5 android: layout_height = "fill_parent"
6>
7 <com.example.multitouch.SomeView
8 android: layout_width = "fill_parent"
9 android: layout_height = "wrap_content"
10 android: id = "@ + id / view"
11 />
12 </ LinearLayout>
13
Our Activity class:

1 package com.example.multitouch;
2
3 import android.app.Activity;
4 import android.os.Bundle;
5
6 public class main extends Activity {
7 / ** Called when the activity is first created. * /
8 @ Override
9 public void onCreate (Bundle savedInstanceState) {
10 super. OnCreate (savedInstanceState);
11 setContentView (R.layout.main);
12}
13}

But the main class - SomeView:
1 package com.example.multitouch;
2
3 import android.content.Context;
4 import android.graphics.Canvas;
5 import android.graphics.Color;
6 import android.graphics.Paint;
7 import android.graphics.Paint.Style;
8 import android.util.AttributeSet;
9 import android.util.Log;
10 import android.view.MotionEvent;
11 import android.view.View;
12
13 / / We draw the yellow circles under your fingers
14 public class SomeView extends View
15 {
16 Paint paint; / / need to set the color.
17 int [] X;
18 int [] Y;
19 final static int Radius = 20;
20 int PointerCount;
21 public SomeView (Context context, AttributeSet attrs)
22 {
23 super (context, attrs);
24 paint = new Paint ();
25 paint.setColor (Color.YELLOW);
26 paint.setStyle (Style.FILL);
27 PointerCount = 0;
28 X = new int [10] / / This will be the arrays of coordinates (we will take up to 10 fingers)
29 Y = new int [10];
30}
31 @ Override
32 public boolean onTouchEvent (MotionEvent event)
33 {
34 StringBuilder result = new StringBuilder (300);
35 PointerCount = event.getPointerCount ();
36 for (int i = 0; i <PointerCount; i + +)
37 {
38 int ptrId = event.getPointerId (i);
39 X [i] = (int) event.getX (i); / / Store the coordinates
40 Y [i] = (int) event.getY (i);
41 result.append («Pointer Index:»). Append (i);
42 result.append (", Pointer Id:"). Append (ptrId). Append ("\ n");
43 result.append («Location:»). Append (event.getX (i));
44 result.append ("x"). Append (event.getX (i)). Append ("\ n");
45 result.append («Pressure:»). Append (event.getPressure (i));
46 result.append («Size:»). Append (event.getSize (i)). Append ("\ n");
47}
48 Log.v («Mytag», result.toString ()); / / Output all the information in the log
49 return true;
50}
51
52 @ Override
53 protected void onDraw (Canvas canvas)
54 {
55 for (int i = 0; i <PointerCount; i + +) / / look how many fingers were on the screen and draw the View
56 {
57 canvas.drawCircle (X [i], Y [i], Radius, paint);
58}
59 invalidate (); / / invalidate () is needed in order to alert the Android, you have to execute a method OnDraw again, without the View will not be pereriovyvatsya.
60}
61
62}
63

Other ways to interact with touchscreen


Interaction with the screen is quite a low level MotionEvent'y need it primarily for game development. For more specific, everyday situations in the Android provides a number of interfaces that allow you to identify the most "popular» touch event'y:
  • OnClickListener - onClick (View v)
  • OnFocusChangeListener - onFocusChange (View v, boolean hasFocus)
  • OnKeyListener - onKey (View v, int keyCode, KeyEvent event)
  • OnLongClickListener - onLongClick (View v)
etc.

They are treated similarly, so that explanation does not need.
More information about these methods can be found here:
developer.android.com / reference / android / view / View.html

Used for this article:
Thank you for your attention.