Partilhar via


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:

Captura de ecrã de exemplo com o botão Tocar em Mim

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:

    Captura de tela da atividade com Touch Begins exibido

  • Agora que confirmamos que a atividade é iniciada, abra o TouchActivity.cs de arquivo e adicione um manipulador para o Touch evento do ImageView:

    _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:

Captura de tela da atividade com o Touch Begins

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:

Captura de tela da atividade com Touch Ends

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 classe MyScaleListener que ajudará a redimensionar o ImageView 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á o ImageView 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 o ImageView. Vamos adicionar uma classe chamada MyScaleListener. Essa classe ouvirá os eventos de escala que serão gerados pelo Android quando o usuário pinçar o ImageView. Adicione a seguinte classe interna ao GestureRecognizerView. Esta classe é um ScaleGesture.SimpleOnScaleGestureListenerarquivo . 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 é GestureRecognizerViewOnTouchEvent. 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:

    Tela inicial do Gesture Recognizer com ícone do Android

  • 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:

    Ícone de movimento de gestos pela 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 um GestureOverlayView e defini-lo como a exibição raiz da Atividade. Também atribuímos um manipulador de eventos ao GesturePerformed evento de GestureOverlayView. Em seguida, inflamos o arquivo de layout que foi criado anteriormente e adicionamos isso como uma exibição filho do GestureOverlayView. 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 gesto GestureOverlayView , ele chama de volta para esse método. A primeira coisa que tentamos obter um IList<Prediction> objeto que corresponda ao gesto chamando _gestureLibrary.Recognize(). Usamos um pouco de LINQ para obter a Prediction 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:

    Captura de tela com a imagem Check Me

    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:

    Marca de seleção desenhada, marca de seleção é reconhecida

    Por fim, desenhe um rabisco na tela. A caixa de seleção deve voltar para sua imagem original, conforme mostrado nestas capturas de tela:

    Rabisco na tela, a imagem original é exibida

Agora você tem uma compreensão de como integrar toque e gestos em um aplicativo Android usando Xamarin.Android.