Статьи

Android SDK: обнаружение жестов

Android SDK поддерживает различные жесты, которые вы можете обнаружить, что позволяет адаптировать способы взаимодействия пользователей с вашими приложениями. В этом уроке мы рассмотрим структуру, которая позволяет разработчикам добавлять обнаружение жестов в простое приложение, прорабатывая процесс поддержки жеста переключения и описывая способы поддержки других жестов. После того, как вы завершили разработку базовой функции fling, вы сможете добавить в приложение дополнительные жесты.


Чтобы продемонстрировать обнаружение жестов в Android, мы создадим простое приложение для отображения сообщений пользователю. Пользователь сможет циклически перемещаться по сообщениям вперед и назад, используя жест переключения , который включает в себя движение пальцем или стилусом по экрану слева направо или справа налево. В действительности, с этим типом приложения содержимое сообщения обычно считывается из источника данных, но для упрощения задачи мы просто сохраняем сообщения в массиве. Создайте новый проект Android и позвольте Eclipse создать для него основной класс Activity с основным макетом.

Откройте макет приложения и используйте следующий основной контент. Можно оставить исходный макет по умолчанию, добавленный Eclipse, на месте и просто настроить содержащийся TextView, чтобы он соответствовал приведенному ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<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»
  android:paddingBottom=»@dimen/activity_vertical_margin»
  android:paddingLeft=»@dimen/activity_horizontal_margin»
  android:paddingRight=»@dimen/activity_horizontal_margin»
  android:paddingTop=»@dimen/activity_vertical_margin»
  tools:context=».MainActivity» >
 
  <TextView
    android:id=»@+id/the_message»
    android:layout_width=»fill_parent»
    android:layout_height=»fill_parent» />
 
</RelativeLayout>

При необходимости измените имя основного класса, указанного в качестве tools: context атрибута для макета, в соответствии с вашим собственным основным классом Activity . Макет включает в себя TextView с атрибутом id, чтобы мы могли ссылаться на него в реализации класса Activity .

Это дает нам базовый макет с текстовым представлением для отображения сообщений. Теперь мы можем реализовать класс Activity . Начните с открытия класса и проверки его содержимого.

01
02
03
04
05
06
07
08
09
10
11
import android.os.Bundle;
import android.app.Activity;
 
public class MainActivity extends Activity {
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }
}

Добавьте следующие операторы импорта в список в верхней части реализации класса.

1
2
3
4
import android.support.v4.view.GestureDetectorCompat;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.TextView;

Подготовим приложение для отображения сообщений. Добавьте следующие переменные экземпляра перед методом onCreate .

1
2
3
4
5
6
7
8
//message text view
private TextView messageView;
//message array
private String[] messages;
//total messages
private int numMessages = 10;
//current message -start at zero
private int currMessage = 0;

Как упоминалось ранее, вы обычно не будете жестко кодировать данные в приложении, но мы сделаем это в этом случае, чтобы сосредоточиться на обнаружении жестов, основной теме этого урока. У нас есть переменная, которая хранит ссылку на текстовое представление, которое мы добавили в макет, массив для сообщений, переменную для записи общего количества сообщений и счетчик для отслеживания того, какое сообщение отображается в данный момент.

Внутри метода onCreate сразу после строки, устанавливающей представление содержимого, извлеките ссылку на текстовое представление, используя его идентификатор, и сохраните ссылку в messageView как показано ниже.

1
messageView = (TextView)findViewById(R.id.the_message);

В следующем фрагменте кода мы создаем экземпляр массива messages.

1
messages = new String[numMessages];

Для простоты давайте сделаем каждое сообщение фиктивным текстом, содержащим число, чтобы мы могли легко видеть, какое сообщение отображается.

01
02
03
04
05
06
07
08
09
10
11
12
for(int i=0; i<numMessages; i++){
  messages[i]=»Message «+i+»:\n\n»+
      «Here is message «+i+»:\n\n»+
      «Lorem ipsum dolor sit amet, consectetur adipisicing elit, «
      + «sed do eiusmod tempor incididunt ut labore et dolore magna «
      + «aliqua. Ut enim ad minim veniam, quis nostrud exercitation «
      + «ullamco laboris nisi ut aliquip ex ea commodo consequat. «
      + «Duis aute irure dolor in reprehenderit in voluptate velit «
      + «esse cillum dolore eu fugiat nulla pariatur. Excepteur sint «
      + «occaecat cupidatat non proident, sunt in culpa qui officia «
      + «deserunt mollit anim id est laborum.»;
}

После завершения цикла установите для параметра «Текстовое представление» отображение первого сообщения массива сообщений.

1
messageView.setText(messages[currMessage]);

У вас есть несколько вариантов, когда речь заходит о подготовке занятий к обнаружению жестов. Вы можете настроить обнаружение жестов для отдельных видов в вашем приложении, установив для них onTouch метод onTouch реализован внутри вашего сенсорного прослушивателя. Чтобы действие в целом могло обнаружить жесты, вы можете заставить класс реализовать интерфейс (ы) GestureDetector и предоставлять методы для каждого жеста. Возможные жесты включают onDoubleTap , onDown , onFling , onLongPress , onScroll , onShowPress и onSingleTapUp . Посмотрите Обнаружение Общих Жестов в Руководстве разработчика для возможных вариантов.

Если вы хотите поддерживать только подмножество жестов, вы можете использовать более удобный вариант, который мы продемонстрируем в этом руководстве, то есть добавить класс, расширяющий SimpleOnGestureListener . Таким образом, класс обрабатывает любые жесты, для которых вы не предоставляете явных инструкций в переопределенных методах.

Добавьте внутреннее объявление класса внутри вашего класса активности после метода onCreate , расширяя SimpleOnGestureListener .

1
2
3
public class GestureListener extends GestureDetector.SimpleOnGestureListener {
//class content
}

Внутри этого класса мы определим, что должно происходить при обнаружении определенного жеста, в нашем примере это жест броска.

Хотя нас в первую очередь интересует жест броска, при использовании SimpleOnGestureListener необходимо как onDown реализовать метод onDown , поскольку в противном случае система может игнорировать оставшиеся события жеста. Добавьте метод в ваш новый внутренний класс, как показано ниже.

1
2
3
4
@Override
public boolean onDown(MotionEvent event) {
  return true;
}

Возвращение true сообщает операционной системе, что ваш код заинтересован в оставшихся событиях жестов. После onDown добавьте реализацию-заглушку для метода onFling . Мы обсудим его реализацию чуть позже в этом уроке.

1
2
3
4
5
@Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
    float velocityX, float velocityY) {
  //determine what happens on fling events
}

Прежде чем мы приступим к деталям того, что должно происходить при обнаружении жеста броска, нам необходимо завершить определение жеста для класса. Добавьте переменную экземпляра для детектора жестов вверху класса активности перед методом onCreate .

1
2
//gesture detector
private GestureDetectorCompat gDetect;

Внутри метода onCreate и после существующего кода onCreate экземпляр детектора жестов, передав экземпляр созданного нами нового класса.

1
gDetect = new GestureDetectorCompat(this, new GestureListener());

После метода onCreate нам нужно переопределить метод onTouchEvent чтобы гарантировать, что прослушиватель жестов используется при обнаружении сенсорных событий.

1
2
3
4
5
@Override
public boolean onTouchEvent(MotionEvent event){
  this.gDetect.onTouchEvent(event);
  return super.onTouchEvent(event);
}

Метод вызывает метод onTouchEvent для детектора жестов, который будет onTouchEvent соответствующие методы жестов внутри внутреннего класса. Далее мы сосредоточимся на деталях того, что произойдет, когда пользователь перебрасывает сообщения в нашем приложении, но уделим немного времени, чтобы взглянуть на структуру вашего класса для дальнейшего использования. Это подход, который вы можете использовать для поддержки набора жестов в любом SimpleOnGestureListener , то есть добавление внутреннего класса для расширения SimpleOnGestureListener , переопределение onTouchEvent для вызова метода onTouchEvent для детектора жестов, который вы создали, используя объект вашего класса SimpleOnGestureListener .


Наконец, мы можем сосредоточиться на том, что происходит при обнаружении броска. В вашем внутреннем классе начните с добавления пары переменных, которые нам необходимы для смещения вычислений для обеспечения оптимальной надежности (до методов onDown и onFling ).

1
2
private float flingMin = 100;
private float velocityMin = 100;

В ваших собственных приложениях может потребоваться настроить эти значения в соответствии с вашими моделями взаимодействия. Внутри onFling объявите две логические переменные, чтобы отслеживать направление перемещения сообщений.

1
2
3
4
//user will move forward through messages on fling up or left
boolean forward = false;
//user will move backward through messages on fling down or right
boolean backward = false;

Мы будем интерпретировать отклонения влево или вверх как желание двигаться вперед, а отклонения вниз или вправо — как желание двигаться назад в сообщениях. Это обычная парадигма UX в приложениях обмена сообщениями и электронной почты.

Обратите внимание, что метод onFling получает четыре параметра, представляющих движения вниз и перемещения, которые инициировали событие, и скорость жеста в направлениях x и y. Мы будем использовать эти параметры, чтобы определить направление броска. Затем мы рассчитываем различия x и y для двух событий движения.

1
2
3
4
//calculate the change in X position within the fling gesture
float horizontalDiff = event2.getX() — event1.getX();
//calculate the change in Y position within the fling gesture
float verticalDiff = event2.getY() — event1.getY();

Теперь вычислите абсолютные значения для этих мер, а также значения скорости.

1
2
3
4
float absHDiff = Math.abs(horizontalDiff);
float absVDiff = Math.abs(verticalDiff);
float absVelocityX = Math.abs(velocityX);
float absVelocityY = Math.abs(velocityY);

Мы хотим отвечать на жест броска только в том случае, если он превышает значения смещения, которые мы определили. Начните со случая, когда горизонтальная разница больше, чем вертикальная разница.

1
2
3
if(absHDiff>absVDiff && absHDiff>flingMin && absVelocityX>velocityMin){
//move forward or backward
}

Внутри блока if установите логические переменные в соответствии с тем, указывает ли жест пользователя движение вперед или назад.

1
2
if(horizontalDiff>0) backward=true;
else forward=true;

Тем не менее, внутри метода onFling , после внешнего оператора if , добавьте else if для случаев, когда горизонтальная разница не больше вертикальной.

1
2
3
4
else if(absVDiff>flingMin && absVelocityY>velocityMin){
  if(verticalDiff>0) backward=true;
  else forward=true;
}

Мы еще раз проверяем, чтобы значения превышали минимальные пороговые значения, которые мы определили ранее, чтобы мы реагировали только на жесты, которые можно разумно интерпретировать как бросающие жесты. Опять же, мы устанавливаем прямую или обратную переменную в true.

По-прежнему внутри onFling обновите отображаемое сообщение в соответствии с жестом пользователя.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
//user is cycling forward through messages
if(forward){
  //check current message is not at end of array
  //increment or set back to start
  if(currMessage<numMessages-1) currMessage++;
  else currMessage = 0;
  //set the message text display
  messageView.setText(messages[currMessage]);
}
//user is cycling backwards through messages
else if(backward){
  //check that current message is not at start of array
  //decrement or set to last message
  if(currMessage>0) currMessage—;
  else currMessage = numMessages-1;
  //set the message text display
  messageView.setText(messages[currMessage]);
}

Если жест указывает на то, что пользователь перемещается вперед по сообщениям, мы сначала проверяем, находятся ли они уже в последнем сообщении, увеличивая переменную или возвращая ее к первому сообщению, а затем устанавливаем текст сообщения для отображения. Если пользователь перемещается в обратном направлении по сообщениям, мы проверяем, отображается ли первое сообщение в данный момент, уменьшая переменную или устанавливая ее в качестве последнего сообщения.

Завершите onFling метода onFling , вернув true .

1
return true;

Теперь вы можете протестировать ваше приложение, запустив его на реальном устройстве или в эмуляторе. Если щелкнуть влево или вверх, отобразится следующее сообщение, а при переходе вправо или вниз — перейти к предыдущему сообщению.

Брошенные сообщения
Приложение после броска влево или вниз трижды с момента запуска.

В этом руководстве мы рассмотрели добавление обнаружения жестов в приложение Android, создание структур, которые вам нужны в ваших классах активности, и проработали простой пример с использованием жеста броска. Посмотрите на исходный код этого учебника, если вы не уверены ни в одной из инструкций. Если вы хотите изучить жесты более подробно, расширьте код, который вы создали, для поддержки других жестов в вашем приложении. Как вы можете видеть, добавление поддержки для обнаружения жестов дает вам хороший контроль над пользовательским интерфейсом в ваших приложениях, хотя какие жесты вам нужно обрабатывать, естественно, будет зависеть от функциональности приложения.