Rastreamento de dedos multitoque
Este tópico demonstra como controlar eventos de toque de vários dedos
Há momentos em que um aplicativo multitoque precisa rastrear dedos individuais enquanto eles se movem simultaneamente na tela. Uma aplicação típica é um programa de pintura a dedo. Você quer que o usuário seja capaz de desenhar com um único dedo, mas também desenhar com vários dedos ao mesmo tempo. Como seu programa processa vários eventos de toque, ele precisa distinguir quais eventos correspondem a cada dedo. O Android fornece um código de identificação para essa finalidade, mas obter e manipular esse código pode ser um pouco complicado.
Para todos os eventos associados a um dedo específico, o código de ID permanece o mesmo. O código de identificação é atribuído quando um dedo toca pela primeira vez na tela e se torna inválido depois que o dedo é levantado da tela. Esses códigos de identificação geralmente são inteiros muito pequenos, e o Android os reutiliza para eventos de toque posteriores.
Quase sempre, um programa que rastreia dedos individuais mantém um dicionário para rastreamento por toque. A chave do dicionário é o código de identificação que identifica um dedo específico. O valor do dicionário depende do aplicativo. No exemplo FingerPaint, cada traçado do dedo (do toque à liberação) é associado a um objeto que contém todas as informações necessárias para renderizar a linha desenhada com esse dedo. O programa define uma pequena FingerPaintPolyline
classe para este fim:
class FingerPaintPolyline
{
public FingerPaintPolyline()
{
Path = new Path();
}
public Color Color { set; get; }
public float StrokeWidth { set; get; }
public Path Path { private set; get; }
}
Cada polilinha tem uma cor, uma largura de traçado e um objeto gráfico Path
Android para acumular e renderizar vários pontos da linha à medida que ela está sendo desenhada.
O restante do código mostrado abaixo está contido em uma View
derivada chamada FingerPaintCanvasView
. Essa classe mantém um dicionário de objetos do tipo FingerPaintPolyline
durante o tempo em que eles estão sendo ativamente desenhados por um ou mais dedos:
Dictionary<int, FingerPaintPolyline> inProgressPolylines = new Dictionary<int, FingerPaintPolyline>();
Este dicionário permite que a visualização obtenha rapidamente as FingerPaintPolyline
informações associadas a um dedo específico.
A FingerPaintCanvasView
classe também mantém um List
objeto para as polilinhas que foram concluídas:
List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();
Os objetos estão List
na mesma ordem em que foram desenhados.
FingerPaintCanvasView
substitui dois métodos definidos por View
: OnDraw
e OnTouchEvent
.
Em sua OnDraw
substituição, a exibição desenha as polilinhas concluídas e, em seguida, desenha as polilinhas em andamento.
A substituição do OnTouchEvent
método começa obtendo um pointerIndex
valor da ActionIndex
propriedade. Esse ActionIndex
valor diferencia entre vários dedos, mas não é consistente em vários eventos. Por esse motivo, use o pointerIndex
para obter o valor de ponteiro id
do GetPointerId
método. Essa ID é consistente em vários eventos:
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;
}
Observe que a substituição usa a ActionMasked
propriedade na instrução em switch
vez da Action
propriedade. Eis o motivo:
Quando você está lidando com multitoque, a Action
propriedade tem um valor de MotionEventsAction.Down
para o primeiro dedo tocar na tela e, em seguida, valores de Pointer2Down
e Pointer3Down
como o segundo e terceiro dedos também tocam na tela. Como o quarto e o quinto dedos fazem contato, a Action
propriedade tem valores numéricos que nem sequer correspondem aos membros da MotionEventsAction
enumeração! Você precisaria examinar os valores dos sinalizadores de bits nos valores para interpretar o que eles significam.
Da mesma forma, como os dedos deixam contato com a tela, a Action
propriedade tem valores de Pointer2Up
e Pointer3Up
para o segundo e terceiro dedos, e Up
para o primeiro dedo.
A ActionMasked
propriedade assume um número menor de valores porque se destina a ser usada em conjunto com a ActionIndex
propriedade para diferenciar entre vários dedos. Quando os dedos tocam na tela, a propriedade só pode ser igual MotionEventActions.Down
para o primeiro dedo e PointerDown
para os dedos subsequentes. Como os dedos saem da tela, ActionMasked
tem valores de Pointer1Up
para os dedos subsequentes e Up
para o primeiro dedo.
Ao usar ActionMasked
o ActionIndex
, o distingue entre os dedos subsequentes para tocar e sair da tela, mas você geralmente não precisa usar esse valor, exceto como um argumento para outros métodos no MotionEvent
objeto. Para multitoque, um dos métodos mais importantes é GetPointerId
chamado no código acima. Esse método retorna um valor que você pode usar para uma chave de dicionário para associar eventos específicos aos dedos.
A OnTouchEvent
substituição no exemplo processa os MotionEventActions.Down
eventos e PointerDown
de forma idêntica, criando um novo FingerPaintPolyline
objeto e adicionando-o ao dicionário:
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;
// ...
}
// ...
}
Observe que o pointerIndex
também é usado para obter a posição do dedo dentro da vista. Todas as informações de toque estão associadas ao pointerIndex
valor. O id
identifica exclusivamente os dedos em várias mensagens, de modo que é usado para criar a entrada do dicionário.
Da mesma forma, a OnTouchEvent
substituição também manipula o MotionEventActions.Up
e Pointer1Up
de forma idêntica, transferindo a polilinha concluída para a completedPolylines
coleção para que eles possam ser desenhados durante a OnDraw
substituição. O código também remove a id
entrada do dicionário:
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;
}
// ...
}
Agora para a parte complicada.
Entre os eventos para baixo e para cima, geralmente há muitos MotionEventActions.Move
eventos. Eles são agrupados em uma única chamada para OnTouchEvent
o , e devem ser tratados de forma diferente dos Down
eventos e Up
. O pointerIndex
valor obtido anteriormente da ActionIndex
propriedade deve ser ignorado. Em vez disso, o método deve obter vários pointerIndex
valores fazendo um loop entre 0 e a PointerCount
propriedade e, em seguida, obter um para cada um id
desses pointerIndex
valores:
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;
// ...
}
// ...
}
Esse tipo de processamento permite que a amostra rastreie dedos individuais e desenhe os resultados na tela:
Agora você viu como você pode rastrear dedos individuais na tela e distinguir entre eles.