- Первый способ является оптимальным, когда вы используете один из стандартных View. У класса View существует метод SetOnTouchListener(View.OnTouchListener l), аргументом которого является класс, реализующий интерфейс OnTouchListener. Этот метод позволяет назначить «слушателя» MotionEvent'ов. Интерфейс OnTouchListener обязует класс реализовывать метод public boolean OnTouch(View v,MotionEvent event), вторым аргументом которого и является нужный нам MotionEvent. Теперь каждый раз, когда пользователь будет прикасаться к экрану, Android будет вызывать наш метод OnTouch, давая ему в аргумент описание произошедшего действия, то есть MotionEvent. Рассмотрим подробнее на примере:
Вот наш файл 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>
А вот наш Activity класс:
1 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
9public class main extends Activity implements OnTouchListener{// Заставляем наш 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);//Достаем нужный View
16 ll.setOnTouchListener(this);// Устанавливаем данный класс в качестве слушателя MotionEvent'ов для нашего LinearLayout
17 }
18
19 @Override
20 public boolean onTouch(View v, MotionEvent event)// Вот, собственно, метод, который и будет обрабатывать MotionEvent'ы.
21 {
22 int Action=event.getAction();
23// С помощью метода getAction() получаем тип действия(ACTION_DOWN,ACTION_MOVE или ACTION_UP)
24 StringBuilder str=new StringBuilder();
25 str.append("\nActrion type: ");
26 //Дальше для лучшего восприятия(т.к. константы ACTION_DOWN,ACTION_MOVE и ACTION_UP числовые)
27 //проводим switch по переменной Action и добавляем в наш StringBuilder название константы
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 //С помощью методов getX() и getY() получаем координаты по оси x и y соответственно
35 //Следует отметить, что точка 0 располагается в левом верхнем углу экрана.
36 //Ось x направлена вправо
37 //Ось y направлена вниз(чем ниже, тем больше координата).
38 str.append(«Location: »).append(event.getX()).append(" x ").append(event.getY()).append("\n");//Узнаем координаты
39 str.append(«Edge flags: »).append(event.getEdgeFlags()).append("\n");// Метод getEdgeFlags возвращает информацию о пересечении краев экрана
40 str.append(«Pressure: »).append(event.getPressure()).append("\n");// Узнаем давление
41 str.append(«Size: »).append(event.getSize()).append("\n"); // Узнаем размер указателя(места соприкосновения пальца с экраном)
42 str.append(«Down time: »).append(event.getDownTime()).append(«ms\n»);// Узнаем время, когда палец был опущен на экран в миллисекундах
43 str.append(«Event time: »).append(event.getEventTime()).append(«ms»);//узнаем текущее время(соответствующее обрабатываемому MotionEvent'у) в миллисекундах
44 str.append(" Elapsed: ").append(event.getEventTime()-event.getDownTime());//Узнаем сколько времени прошло с момента опускания пальца, до текущего MotionEvent'а
45 Log.v(«Mytag», str.toString());//Для того, чтобы можно было отслеживать эти действия, записываем всю информацию о них в лог.
46 return true;// Почему мы возвращаем true будет рассмотрено потом
47 }
48 }
Понятно, что один класс может быть слушателем у нескольких View. Чтобы это учитывать метод onTouch принимает в аргумент View. - Второй способ является оптимальным, когда вы пишете свой View класс, который должен реагировать на прикосновения. У View есть метод onTouchEvent(MotionEvent event), который, собственно, и позволяет обрабатывать MotionEvent'ы. Рассмотрим на примере:
Напишем свой класс 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 //Сначала необходимо реализовать конструктор.
11 //Тут все просто - просто вызываем конструктор суперкласса
12 public SomeView(Context context, AttributeSet attrs)
13 {
14 super(context,attrs);
15 }
16
17 //Теперь приступаем непосредственно к обработке MotionEvent'ов.
18 //Для этого нужно переписать метод 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
И наш /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 класс:
1 package com.example.MotionEventExample1;
1 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 }
О том, что возвращают обработчики MotionEvent'ов
Как вы могли заметить, обработчики MotionEvent'ов должны возвращать boolean'овское значение. В примерах до этого мы просто всегда возвращали true. Так для чего же этим методам вообще нужно возвращать значение? Дело в том, что у одного View может быть несколько слушателей (например onTouchEvent в самом View и OnTouch у внешнего слушателя) и они имеют некоторый приоритет: OnTouch вызывается первым (если он вообще имеется), а уже после него может вызываться onTouchEvent. Вызов следующего по приоритету обработчика как раз зависит от возвращаемого текущим обработчиком значения (true — ничто не вызывается, false — вызывается следующий по приоритету, если таковой имеется). Таким образом Android позволяет нам разделять обязанности по обработке touch event'ов на несколько слушателей.
Drag and Drop
Вот мы и разобрались с основами обработки MotionEvento'ов. Для закрепления навыков напишем приложение, реализующее простенький Drag and Drop.
Вот наш /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>
Вот наш 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 }
А вот наш основной класс 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 вызвается Андроидом тогда, когда нужно отрисовать данный View
37 {
38 canvas.drawCircle(X, Y, Radius, paint);
39 invalidate();// invalidate() нужен для того, чтобы оповестить Android, что нужно выполнить метод OnDraw снова, без него View не будет перериcовываться.
40 }
41
42 }
Multitouch
В Android, сколько-бы вы пальцев ни использовали, информация о всех них хранится в одном MotionEvent'е. Первый метод, который нужно узнать, если вы собираетесь использовать мультитач — getPointerCount(), который возвращает количество пальцев, зафиксированных на экране(Не всегда совпадает с реальным количеством из-за ограничений в железе). Каждому пальцу, находящемуся на экране, присваивается индекс и id. Индексы всегда начинаются с 0, id — не обязательно. Для понятности рассмотрим на примере.
Первый палец опускается — ему присваивается index=0 и id=0
Второй палец опускается — ему присваивается index=1 и id=1
Первый палец поднимается — Второму пальцу присваивается index=0 (Индексы всегда начинаются с нуля), а id остается тем же
Второй палец поднимается — MotionEvent'ов больше нет, вплоть до следушего прикосновения.
Id всегда сохраняется за указателем(пальцем), так что мы всегда можем отследить какой угодно указатель.
Когда мы разобрались с id и индексами, рассмотрим как получать свойства (координаты, величину и т. п. ) у конкретного указателя, когда их много. Для этого в классе MotionEvent существуют методы:
- getX(int Index)
- getY(int Index)
- getSize(int Index)
В аргумент эти методы принимают индекс указателя.
Но так как индексы указателя могут меняться, а часто требуется отследить конкретный палец, то существует очень важный метод getPointerId(int index), который позволяет по указанному индексу узнать id указателя.
Рассмотрим пример: перепишем наше приложение (Drag and Drop) так, чтобы оно воспринимало мультитач.
(Важно: На эмуляторе мультитач не проверить, это можно сделать только на настоящем девайсе)
Вот наш 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
Наш Activity класс:
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 }
А вот основной класс — 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 //Будем рисовать желтые круги под пальцами
14 public class SomeView extends View
15 {
16 Paint paint;//нужен чтобы установить цвет.
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];//Это будут массивы координат(будем воспринимать до 10 пальцев)
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);// Запоминаем координаты
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());//Выводим всю информацию в лог
49 return true;
50 }
51
52 @Override
53 protected void onDraw(Canvas canvas)
54 {
55 for(int i=0;i<PointerCount;i++)//Смотрим сколько пальцев было на экране и отрисовываем View
56 {
57 canvas.drawCircle(X[i], Y[i], Radius, paint);
58 }
59 invalidate();// invalidate() нужен для того, чтобы оповестить Android, что нужно выполнить метод OnDraw снова, без него View не будет перериовываться.
60 }
61
62 }
63
Другие способы взаимодействия с тачскрином
Взаимодействие с экраном через MotionEvent'ы является довольно низкоуровневым, нужно это прежде всего для разработки игр. Для более узких, повседневных ситуаций в Android предусмотрен ряд интерфейсов, которые позволяют распознавать наиболее «популярные» touch event'ы:
- OnClickListener — onClick(View v)
- OnFocusChangeListener — onFocusChange(View v, boolean hasFocus)
- OnKeyListener — onKey(View v, int keyCode, KeyEvent event)
- OnLongClickListener — onLongClick(View v)
Обрабатываются они аналогично, так что в пояснении не нуждаются.
Больше информации о таких методах можно узнать здесь:
developer.android.com/reference/android/view/View.html
Комментариев нет:
Отправить комментарий