Passo a passo - Usando o Touch no Android
Vamos ver como usar os conceitos da seção anterior em um aplicativo de trabalho. Vamos criar um aplicativo com quatro atividades. A primeira atividade será um menu ou um quadro de distribuição que iniciará as outras atividades para demonstrar as várias APIs. A captura de tela a seguir mostra a atividade principal:
A primeira Atividade, Exemplo de Toque, mostrará como usar manipuladores de eventos para tocar nos Modos de Exibição. A atividade Reconhecedor de gestos demonstrará como subclassificar Android.View.Views
e manipular eventos, bem como mostrará como lidar com gestos de pinça. A terceira e última atividade, Gesto Personalizado, mostrará como usar gestos personalizados. Para tornar as coisas mais fáceis de seguir e absorver, dividiremos este passo a passo em seções, com cada seção se concentrando em uma das Atividades.
Atividade de amostra de toque
Abra o TouchWalkthrough_Start do projeto. A MainActivity está pronta para funcionar – cabe a nós implementar o comportamento de toque na atividade. Se você executar o aplicativo e clicar em Touch Sample, a seguinte atividade deverá ser iniciada:
Agora que confirmamos que a atividade é iniciada, abra o TouchActivity.cs de arquivo e adicione um manipulador para o
Touch
evento doImageView
:_touchMeImageView.Touch += TouchMeImageViewOnTouch;
Em seguida, adicione o seguinte método ao TouchActivity.cs:
private void TouchMeImageViewOnTouch(object sender, View.TouchEventArgs touchEventArgs) { string message; switch (touchEventArgs.Event.Action & MotionEventActions.Mask) { case MotionEventActions.Down: case MotionEventActions.Move: message = "Touch Begins"; break; case MotionEventActions.Up: message = "Touch Ends"; break; default: message = string.Empty; break; } _touchInfoTextView.Text = message; }
Observe no código acima que tratamos a Move
ação como Down
a mesma. Isso porque, mesmo que o usuário não levante o dedo do ImageView
, ele pode se movimentar ou a pressão exercida pelo usuário pode mudar. Esses tipos de alterações gerarão uma Move
ação.
Cada vez que o usuário tocar no ImageView
, o Touch
evento será gerado e nosso manipulador exibirá a mensagem Touch Begins na tela, conforme mostrado na captura de tela a seguir:
Enquanto o usuário estiver tocando no ImageView
, Touch Begins será exibido no TextView
. Quando o usuário não estiver mais tocando no ImageView
, a mensagem Touch Ends será exibida no TextView
, conforme mostrado na captura de tela a seguir:
Atividade do Reconhecedor de Gestos
Agora vamos implementar a atividade Gesture Recognizer. Esta atividade demonstrará como arrastar uma exibição pela tela e ilustrará uma maneira de implementar o pinça-para-zoom.
Adicione uma nova Atividade ao aplicativo chamado
GestureRecognizer
. Edite o código para essa atividade para que ele se pareça com o seguinte código:public class GestureRecognizerActivity : Activity { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); View v = new GestureRecognizerView(this); SetContentView(v); } }
Adicione um novo modo de exibição do Android ao projeto e nomeie-o
GestureRecognizerView
. Adicione as seguintes variáveis a esta classe:private static readonly int InvalidPointerId = -1; private readonly Drawable _icon; private readonly ScaleGestureDetector _scaleDetector; private int _activePointerId = InvalidPointerId; private float _lastTouchX; private float _lastTouchY; private float _posX; private float _posY; private float _scaleFactor = 1.0f;
Adicione o seguinte construtor ao
GestureRecognizerView
. Este construtor irá adicionar um àImageView
nossa atividade. Neste ponto, o código ainda não será compilado – precisamos criar a classeMyScaleListener
que ajudará a redimensionar oImageView
quando o usuário o pinçar:public GestureRecognizerView(Context context): base(context, null, 0) { _icon = context.Resources.GetDrawable(Resource.Drawable.Icon); _icon.SetBounds(0, 0, _icon.IntrinsicWidth, _icon.IntrinsicHeight); _scaleDetector = new ScaleGestureDetector(context, new MyScaleListener(this)); }
Para desenhar a imagem em nossa atividade, precisamos substituir o
OnDraw
método da classe View, conforme mostrado no trecho a seguir. Esse código moverá oImageView
para a posição especificada por_posX
e_posY
também redimensionará a imagem de acordo com o fator de escala:protected override void OnDraw(Canvas canvas) { base.OnDraw(canvas); canvas.Save(); canvas.Translate(_posX, _posY); canvas.Scale(_scaleFactor, _scaleFactor); _icon.Draw(canvas); canvas.Restore(); }
Em seguida, precisamos atualizar a variável
_scaleFactor
de instância à medida que o usuário pinça oImageView
. Vamos adicionar uma classe chamadaMyScaleListener
. Essa classe ouvirá os eventos de escala que serão gerados pelo Android quando o usuário pinçar oImageView
. Adicione a seguinte classe interna aoGestureRecognizerView
. Esta classe é umScaleGesture.SimpleOnScaleGestureListener
arquivo . Essa classe é uma classe de conveniência que os ouvintes podem subclassificar quando você estiver interessado em um subconjunto de gestos:private class MyScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener { private readonly GestureRecognizerView _view; public MyScaleListener(GestureRecognizerView view) { _view = view; } public override bool OnScale(ScaleGestureDetector detector) { _view._scaleFactor *= detector.ScaleFactor; // put a limit on how small or big the image can get. if (_view._scaleFactor > 5.0f) { _view._scaleFactor = 5.0f; } if (_view._scaleFactor < 0.1f) { _view._scaleFactor = 0.1f; } _view.Invalidate(); return true; } }
O próximo método que precisamos substituir é
GestureRecognizerView
OnTouchEvent
. O código a seguir lista a implementação completa desse método. Há muito código aqui, então vamos dar um minuto e ver o que está acontecendo aqui. A primeira coisa que esse método faz é dimensionar o ícone, se necessário – isso é tratado chamando_scaleDetector.OnTouchEvent
. Em seguida, tentamos descobrir qual ação chamou esse método:Se o usuário tocou na tela com, gravamos as posições X e Y e o ID do primeiro ponteiro que tocou na tela.
Se o usuário moveu seu toque na tela, então descobrimos até onde o usuário moveu o ponteiro.
Se o usuário tiver retirado o dedo da tela, pararemos de rastrear os gestos.
public override bool OnTouchEvent(MotionEvent ev) { _scaleDetector.OnTouchEvent(ev); MotionEventActions action = ev.Action & MotionEventActions.Mask; int pointerIndex; switch (action) { case MotionEventActions.Down: _lastTouchX = ev.GetX(); _lastTouchY = ev.GetY(); _activePointerId = ev.GetPointerId(0); break; case MotionEventActions.Move: pointerIndex = ev.FindPointerIndex(_activePointerId); float x = ev.GetX(pointerIndex); float y = ev.GetY(pointerIndex); if (!_scaleDetector.IsInProgress) { // Only move the ScaleGestureDetector isn't already processing a gesture. float deltaX = x - _lastTouchX; float deltaY = y - _lastTouchY; _posX += deltaX; _posY += deltaY; Invalidate(); } _lastTouchX = x; _lastTouchY = y; break; case MotionEventActions.Up: case MotionEventActions.Cancel: // We no longer need to keep track of the active pointer. _activePointerId = InvalidPointerId; break; case MotionEventActions.PointerUp: // check to make sure that the pointer that went up is for the gesture we're tracking. pointerIndex = (int) (ev.Action & MotionEventActions.PointerIndexMask) >> (int) MotionEventActions.PointerIndexShift; int pointerId = ev.GetPointerId(pointerIndex); if (pointerId == _activePointerId) { // This was our active pointer going up. Choose a new // action pointer and adjust accordingly int newPointerIndex = pointerIndex == 0 ? 1 : 0; _lastTouchX = ev.GetX(newPointerIndex); _lastTouchY = ev.GetY(newPointerIndex); _activePointerId = ev.GetPointerId(newPointerIndex); } break; } return true; }
Agora execute o aplicativo e inicie a atividade do Reconhecedor de Gestos. Quando ele é iniciado, a tela deve se parecer com a captura de tela abaixo:
Agora toque no ícone e arraste-o pela tela. Experimente o gesto de apertar para ampliar. Em algum momento, sua tela pode se parecer com a seguinte captura de tela:
Neste ponto, você deve dar um tapinha nas costas: você acabou de implementar o pinch-to-zoom em um aplicativo Android! Faça uma pausa rápida e vamos passar para a terceira e última Atividade neste passo a passo – usando gestos personalizados.
Atividade de gesto personalizado
A tela final neste passo a passo usará gestos personalizados.
Para os fins deste Passo a passo, a biblioteca de gestos já foi criada usando a Ferramenta de Gestos e adicionada ao projeto no arquivo Recursos/raw/gestos. Com este pouco de limpeza fora do caminho, vamos continuar com a atividade final no passo a passo.
Adicione um arquivo de layout chamado custom_gesture_layout.axml ao projeto com o conteúdo a seguir. O projeto já tem todas as imagens na pasta Recursos :
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <ImageView android:src="@drawable/check_me" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="3" android:id="@+id/imageView1" android:layout_gravity="center_vertical" /> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> </LinearLayout>
Em seguida, adicione uma nova Atividade ao projeto e nomeie-a
CustomGestureRecognizerActivity.cs
. Adicione duas variáveis de instância à classe, conforme mostrado nas duas linhas de código a seguir:private GestureLibrary _gestureLibrary; private ImageView _imageView;
Edite o
OnCreate
método desta atividade para que ele se pareça com o código a seguir. Vamos tirar um minuto para explicar o que está acontecendo neste código. A primeira coisa que fazemos é instanciar umGestureOverlayView
e defini-lo como a exibição raiz da Atividade. Também atribuímos um manipulador de eventos aoGesturePerformed
evento deGestureOverlayView
. Em seguida, inflamos o arquivo de layout que foi criado anteriormente e adicionamos isso como uma exibição filho doGestureOverlayView
. A etapa final é inicializar a variável_gestureLibrary
e carregar o arquivo de gestos dos recursos do aplicativo. Se o arquivo de gestos não puder ser carregado por algum motivo, não há muito que essa atividade possa fazer, então ele é desligado:protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); GestureOverlayView gestureOverlayView = new GestureOverlayView(this); SetContentView(gestureOverlayView); gestureOverlayView.GesturePerformed += GestureOverlayViewOnGesturePerformed; View view = LayoutInflater.Inflate(Resource.Layout.custom_gesture_layout, null); _imageView = view.FindViewById<ImageView>(Resource.Id.imageView1); gestureOverlayView.AddView(view); _gestureLibrary = GestureLibraries.FromRawResource(this, Resource.Raw.gestures); if (!_gestureLibrary.Load()) { Log.Wtf(GetType().FullName, "There was a problem loading the gesture library."); Finish(); } }
A última coisa que precisamos fazer é implementar o método
GestureOverlayViewOnGesturePerformed
, conforme mostrado no trecho de código a seguir. Quando o detecta um gestoGestureOverlayView
, ele chama de volta para esse método. A primeira coisa que tentamos obter umIList<Prediction>
objeto que corresponda ao gesto chamando_gestureLibrary.Recognize()
. Usamos um pouco de LINQ para obter aPrediction
pontuação mais alta para o gesto.Se não houver nenhum gesto de correspondência com uma pontuação alta o suficiente, o manipulador de eventos será encerrado sem fazer nada. Caso contrário, verificamos o nome da previsão e alteramos a imagem que está sendo exibida com base no nome do gesto:
private void GestureOverlayViewOnGesturePerformed(object sender, GestureOverlayView.GesturePerformedEventArgs gesturePerformedEventArgs) { IEnumerable<Prediction> predictions = from p in _gestureLibrary.Recognize(gesturePerformedEventArgs.Gesture) orderby p.Score descending where p.Score > 1.0 select p; Prediction prediction = predictions.FirstOrDefault(); if (prediction == null) { Log.Debug(GetType().FullName, "Nothing seemed to match the user's gesture, so don't do anything."); return; } Log.Debug(GetType().FullName, "Using the prediction named {0} with a score of {1}.", prediction.Name, prediction.Score); if (prediction.Name.StartsWith("checkmark")) { _imageView.SetImageResource(Resource.Drawable.checked_me); } else if (prediction.Name.StartsWith("erase", StringComparison.OrdinalIgnoreCase)) { // Match one of our "erase" gestures _imageView.SetImageResource(Resource.Drawable.check_me); } }
Execute o aplicativo e inicie a atividade Custom Gesture Recognizer. Ele deve se parecer com a seguinte captura de tela:
Agora desenhe uma marca de seleção na tela, e o bitmap que está sendo exibido deve ser semelhante ao mostrado nas próximas capturas de tela:
Por fim, desenhe um rabisco na tela. A caixa de seleção deve voltar para sua imagem original, conforme mostrado nestas capturas de tela:
Agora você tem uma compreensão de como integrar toque e gestos em um aplicativo Android usando Xamarin.Android.