четверг, 19 июля 2012 г.

http://habrahabr.ru/post/111405/


  • Первый способ является оптимальным, когда вы используете один из стандартных 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.0encoding="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.0encoding="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.0encoding="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.0encoding="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

Комментариев нет:

Отправить комментарий