Отслеживание нескольких сенсорных пальцев
В этом разделе показано, как отслеживать события касания с нескольких пальцев
Иногда требуется отслеживать отдельные пальцы при одновременном перемещении на экране. Одно из типичных приложений — это программа с пальцем. Вы хотите, чтобы пользователь мог рисовать с одним пальцем, а также рисовать с несколькими пальцами одновременно. При обработке нескольких сенсорных событий программа должна различать, какие события соответствуют каждому пальцу. Android предоставляет код идентификатора для этой цели, но получение и обработка этого кода может быть немного сложной.
Для всех событий, связанных с определенным пальцем, код идентификатора остается неизменным. Код идентификатора назначается, когда пальцем сначала прикасается к экрану, и становится недействительным после того, как палец поднимается с экрана. Эти коды идентификаторов обычно являются очень небольшими целыми числами, и Android повторно использует их для последующих событий касания.
Почти всегда программа, которая отслеживает отдельные пальцы, поддерживает словарь для отслеживания сенсорного ввода. Ключ словаря — это код идентификатора, определяющий определенный палец. Значение словаря зависит от приложения. В примере FingerPaint каждый штрих пальца (от касания к выпуску) связан с объектом, который содержит все сведения, необходимые для отрисовки линии, нарисованной с помощью этого пальца. Программа определяет небольшой FingerPaintPolyline
класс для этой цели:
class FingerPaintPolyline
{
public FingerPaintPolyline()
{
Path = new Path();
}
public Color Color { set; get; }
public float StrokeWidth { set; get; }
public Path Path { private set; get; }
}
Каждая полилайн имеет цвет, ширину штриха и графический Path
объект Android для накапливания и отрисовки нескольких точек линии по мере рисования.
Оставшаяся часть кода, показанная ниже, содержится в производном именованном View
FingerPaintCanvasView
коде. Этот класс поддерживает словарь объектов типа FingerPaintPolyline
во время их активного рисования одним или несколькими пальцами:
Dictionary<int, FingerPaintPolyline> inProgressPolylines = new Dictionary<int, FingerPaintPolyline>();
Этот словарь позволяет представлению быстро получать FingerPaintPolyline
сведения, связанные с определенным пальцем.
Класс FingerPaintCanvasView
также поддерживает List
объект для завершенных полилайнов:
List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();
Объекты в этом List
порядке находятся в том же порядке, что и они были нарисованы.
FingerPaintCanvasView
переопределяет два метода, определенных в следующих View
методах: OnDraw
и OnTouchEvent
.
В его OnDraw
переопределении представление рисует завершенные полилайны, а затем рисует выполняемые полилайны.
Переопределение OnTouchEvent
метода начинается с получения pointerIndex
значения из ActionIndex
свойства. Это ActionIndex
значение отличается от нескольких пальцев, но оно не согласовано в нескольких событиях. По этой причине используется pointerIndex
для получения значения указателя id
из GetPointerId
метода. Этот идентификатор согласован в нескольких событиях:
public override bool OnTouchEvent(MotionEvent args)
{
// Get the pointer index
int pointerIndex = args.ActionIndex;
// Get the id to identify a finger over the course of its progress
int id = args.GetPointerId(pointerIndex);
// Use ActionMasked here rather than Action to reduce the number of possibilities
switch (args.ActionMasked)
{
// ...
}
// Invalidate to update the view
Invalidate();
// Request continued touch input
return true;
}
Обратите внимание, что переопределение использует ActionMasked
свойство в switch
инструкции, а не Action
свойство. Для этого есть следующие причины.
При работе с несколькими сенсорными Action
экранами свойство имеет значение MotionEventsAction.Down
для первого пальца, чтобы коснуться экрана, а затем значения Pointer2Down
и Pointer3Down
как второй и третий пальцы также касаются экрана. Как четвертый и пятый пальцы делают контакт, свойство имеет числовые значения, Action
которые даже не соответствуют членам MotionEventsAction
перечисления! Необходимо проверить значения битовых флагов в значениях, чтобы интерпретировать то, что они означают.
Аналогичным образом, как пальцы покидают контакт с экраном, Action
свойство имеет значения Pointer2Up
и Pointer3Up
для второго и третьего пальцев, а Up
также для первого пальца.
Свойство ActionMasked
занимает меньшее количество значений, так как оно предназначено для использования в сочетании со ActionIndex
свойством для различения нескольких пальцев. Когда пальцы касаются экрана, свойство может быть равно MotionEventActions.Down
только первому пальцу и PointerDown
последующим пальцам. По мере того как пальцы покидают экран, ActionMasked
имеют значения Pointer1Up
для последующих пальцев и Up
для первого пальца.
При использовании ActionMasked
, ActionIndex
различает следующие пальцы для касания и выхода из экрана, но обычно не нужно использовать это значение, за исключением аргумента других методов в объекте MotionEvent
. Для нескольких касаний один из наиболее важных из этих методов вызывается GetPointerId
в приведенном выше коде. Этот метод возвращает значение, которое можно использовать для ключа словаря для связывания определенных событий с пальцами.
Переопределение OnTouchEvent
в примере обрабатывает MotionEventActions.Down
и PointerDown
события идентично путем создания нового FingerPaintPolyline
объекта и добавления его в словарь:
public override bool OnTouchEvent(MotionEvent args)
{
// Get the pointer index
int pointerIndex = args.ActionIndex;
// Get the id to identify a finger over the course of its progress
int id = args.GetPointerId(pointerIndex);
// Use ActionMasked here rather than Action to reduce the number of possibilities
switch (args.ActionMasked)
{
case MotionEventActions.Down:
case MotionEventActions.PointerDown:
// Create a Polyline, set the initial point, and store it
FingerPaintPolyline polyline = new FingerPaintPolyline
{
Color = StrokeColor,
StrokeWidth = StrokeWidth
};
polyline.Path.MoveTo(args.GetX(pointerIndex),
args.GetY(pointerIndex));
inProgressPolylines.Add(id, polyline);
break;
// ...
}
// ...
}
Обратите внимание, что он pointerIndex
также используется для получения положения пальца в представлении. Все сенсорные данные связаны со значением pointerIndex
. Уникально id
идентифицирует пальцы между несколькими сообщениями, поэтому используется для создания записи словаря.
OnTouchEvent
Аналогичным образом переопределение также обрабатывает MotionEventActions.Up
и Pointer1Up
идентично путем передачи завершенной полилайн в completedPolylines
коллекцию, чтобы они могли быть нарисованы во время OnDraw
переопределения. Код также удаляет id
запись из словаря:
public override bool OnTouchEvent(MotionEvent args)
{
// ...
switch (args.ActionMasked)
{
// ...
case MotionEventActions.Up:
case MotionEventActions.Pointer1Up:
inProgressPolylines[id].Path.LineTo(args.GetX(pointerIndex),
args.GetY(pointerIndex));
// Transfer the in-progress polyline to a completed polyline
completedPolylines.Add(inProgressPolylines[id]);
inProgressPolylines.Remove(id);
break;
case MotionEventActions.Cancel:
inProgressPolylines.Remove(id);
break;
}
// ...
}
Теперь для сложной части.
Между событиями вниз и вверх, как правило, есть много MotionEventActions.Move
событий. Они упаковываются в один вызов OnTouchEvent
, и они должны обрабатываться по-разному от Down
событий и Up
событий. Значение, полученное pointerIndex
ActionIndex
ранее из свойства, должно игнорироваться. Вместо этого метод должен получить несколько pointerIndex
значений, циклив между 0 и PointerCount
свойством, а затем получить id
для каждого из этих pointerIndex
значений:
public override bool OnTouchEvent(MotionEvent args)
{
// ...
switch (args.ActionMasked)
{
// ...
case MotionEventActions.Move:
// Multiple Move events are bundled, so handle them differently
for (pointerIndex = 0; pointerIndex < args.PointerCount; pointerIndex++)
{
id = args.GetPointerId(pointerIndex);
inProgressPolylines[id].Path.LineTo(args.GetX(pointerIndex),
args.GetY(pointerIndex));
}
break;
// ...
}
// ...
}
Этот тип обработки позволяет образцу отслеживать отдельные пальцы и выводить результаты на экране:
Теперь вы узнали, как можно отслеживать отдельные пальцы на экране и различать их.