Mobile

21 mai, 2019

Xamarin Rocket – Parte 07: gestos

Publicidade

Fala, galera!

Tentando manter a periodicidade semanal, chegamos novamente com uma dica rápida de Xamarin.Forms!

A ideia é ser um artigo bem rápido, e todas as dicas ficarão em um único repositório. Se você perdeu algum artigo, veja os que já foram publicados:

Arrastar, dar zoom e movimento pinça – diversos gestos que fazemos em muitos dos apps do nosso dia a dia.

A utilização de gestos se mostra muito útil, como dar zoom em uma imagem, por exemplo.

Xamarin.Forms dá suporte a diversos gestos muito utilizados, e a implementação dos mesmos é muito simples. Então, sem mais introduções, vamos à alguns deles:

Antes, vamos preparar nosso app adicionando duas propriedades doubles auxiliares no APP.xaml.cs para guardarmos o tamanho da tela:

 public partial class App : Application
    {
        public static double ScreenWidth;
        public static double ScreenHeight;

        public App()
        {
            InitializeComponent();

            MainPage = new NavigationPage( new MainPage());
        }

    }

Vamos aos gestos:

PanGesture: o “gesto de panorâmica” é utilizado para detectar a movimentação dos dedos na tela e mover seu conteúdo. Por exemplo, se temos uma imagem maior que a tela, podemos utilizar esse gesto para percorrê-la.

Criaremos uma classe para encapsular o gesto e utilizarmos como controle:

using System;
using Xamarin.Forms;

namespace XamarinRocket.Control.Gestos
{
    public class PanContainer : ContentView
    {
        double x, y;

        public PanContainer()
        {
            var panGesture = new PanGestureRecognizer();
            panGesture.PanUpdated += OnPanUpdated;
            GestureRecognizers.Add(panGesture);
        }

        void OnPanUpdated(object sender, PanUpdatedEventArgs e)
        {
            switch (e.StatusType)
            {

                case GestureStatus.Running:
                    Content.TranslationX = Math.Max(Math.Min(0, x + e.TotalX), -Math.Abs(Content.Width - App.ScreenWidth));
                    Content.TranslationY = Math.Max(Math.Min(0, y + e.TotalY), -Math.Abs(Content.Height - App.ScreenHeight));
                    break;

                case GestureStatus.Completed:
                    x = Content.TranslationX;
                    y = Content.TranslationY;
                    break;
            }
        }
    }
}

O evento OnPanUpdated é atualizado toda vez que movemos o conteúdo (no caso, a imagem).

Agora implementaremos o controle e nossa imagem:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="XamarinRocket.Views.Gestos.PanGesturePage" xmlns:local="clr-namespace:XamarinRocket.Control.Gestos">
   <ContentPage.Content>
        <AbsoluteLayout>
            <local:PanContainer>
                <Image Source="xamarinRocket.png" WidthRequest="1024" HeightRequest="768" />
            </local:PanContainer>
        </AbsoluteLayout>
    </ContentPage.Content>
</ContentPage>

Rodando, podemos mover a imagem:

PinchGesture: o famoso movimento pinça, que utilizamos dois dedos, e o movimento de abrir e fechar. É muito útil para dar zoom em imagens, por exemplo.

Vamos criar uma classe para encapsular o gesto e utilizarmos como controle:

using System;
using Xamarin.Forms;
using Xamarin.Forms.Internals;

namespace XamarinRocket.Control.Gestos
{
    public class PinchToZoomContainer : ContentView
    {
        double currentScale = 1;
        double startScale = 1;
        double xOffset = 0;
        double yOffset = 0;

        public PinchToZoomContainer()
        {
            var pinchGesture = new PinchGestureRecognizer();
            pinchGesture.PinchUpdated += OnPinchUpdated;
            GestureRecognizers.Add(pinchGesture);
        }

        void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
        {
            if (e.Status == GestureStatus.Started)
            {
                startScale = Content.Scale;
                Content.AnchorX = 0;
                Content.AnchorY = 0;
            }
            if (e.Status == GestureStatus.Running)
            {
                currentScale += (e.Scale - 1) * startScale;
                currentScale = Math.Max(1, currentScale);

                double renderedX = Content.X + xOffset;
                double deltaX = renderedX / Width;
                double deltaWidth = Width / (Content.Width * startScale);
                double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;

                double renderedY = Content.Y + yOffset;
                double deltaY = renderedY / Height;
                double deltaHeight = Height / (Content.Height * startScale);
                double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;

                double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
                double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);

                Content.TranslationX = targetX.Clamp(-Content.Width * (currentScale - 1), 0);
                Content.TranslationY = targetY.Clamp(-Content.Height * (currentScale - 1), 0);

                Content.Scale = currentScale;
            }
            if (e.Status == GestureStatus.Completed)
            {
                xOffset = Content.TranslationX;
                yOffset = Content.TranslationY;
            }
        }
    }
}

Também implementaremos o controle na imagem:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinRocket.Views.Gestos.PinchGesturePage" xmlns:local="clr-namespace:XamarinRocket.Control.Gestos">
   <ContentPage.Content>
        <Grid Padding="20">
            <local:PinchToZoomContainer>
                <local:PinchToZoomContainer.Content>
                    <Image Source="xamarinRocket.png" />
                </local:PinchToZoomContainer.Content>
            </local:PinchToZoomContainer>
        </Grid>
    </ContentPage.Content>
</ContentPage>

Rodando, podemos dar zoom na imagem:

SwipeGesture: o movimento de passar o dedo para executar alguma ação. É possível identificar a direção que o dedo é passado – para cima, para baixo, para direta ou esquerda.

Criaremos uma classe para encapsular o gesto e utilizar como controle:

using System;
using Xamarin.Forms;

namespace XamarinRocket.Control.Gestos
{
    public class SwipeContainer : ContentView
    {
        public event EventHandler<SwipedEventArgs> Swipe;

        public SwipeContainer()
        {
            GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Left));
            GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Right));
            GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Up));
            GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Down));
        }

        SwipeGestureRecognizer GetSwipeGestureRecognizer(SwipeDirection direction)
        {
            var swipe = new SwipeGestureRecognizer { Direction = direction };
            swipe.Swiped += (sender, e) => Swipe?.Invoke(this, e);
            return swipe;
        }
    }
}

Também vamos implementar o controle em um boxView:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinRocket.Views.Gestos.SwipeGesturePage" xmlns:local="clr-namespace:XamarinRocket.Control.Gestos">
    <StackLayout Margin="20">
        <Label Text="Passe o dedo em qualquer direção no box." />
        <local:SwipeContainer Swipe="OnSwiped" HorizontalOptions="Center" VerticalOptions="CenterAndExpand">
            <BoxView Color="Teal" WidthRequest="300" HeightRequest="300" /> 
        </local:SwipeContainer>
        <Label x:Name="_label" Text="Voce moveu para: "/>
    </StackLayout>
</ContentPage>

E no back-end:

using System;
using System.Collections.Generic;

using Xamarin.Forms;

namespace XamarinRocket.Views.Gestos
{
    public partial class SwipeGesturePage : ContentPage
    {
        public SwipeGesturePage()
        {
            InitializeComponent();
        }

        void OnSwiped(object sender, SwipedEventArgs e)
        {
            _label.Text = $"Você moveu para: {e.Direction.ToString()}";
        }
    }
}

Rodando, podemos testar passando o dedo no box, ou o cursor no emulador:

TapGesture: o gesto de tocar. Com ele, podemos executar algum evento quando é tocado em algum controle na tela, como em uma imagem, por exemplo.

Para utilizar, basta adicionar a propriedade de gesto no controle desejado, como nessa imagem, por exemplo:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinRocket.Views.Gestos.TapGesturePage">
   <ContentPage.Content>
        <StackLayout Padding="20">
          
         <Image HeightRequest="350" WidthRequest="350" Source="xamarinRocket.png" >
              <Image.GestureRecognizers>
                    <TapGestureRecognizer Tapped="Image_Tapped" ></TapGestureRecognizer>
              </Image.GestureRecognizers>
         </Image>
         
            <Label x:Name="lblTapedCount" Text="Você tocou:"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Agora, no back-end, vamos implementar um evento para ver quantas vezes foram tocadas na imagem:

using System;
using System.Collections.Generic;

using Xamarin.Forms;

namespace XamarinRocket.Views.Gestos
{
    public partial class TapGesturePage : ContentPage
    {

        int tapCount;
        public TapGesturePage()
        {
            InitializeComponent();
        }

        public void Image_Tapped(object sender, EventArgs e)
        {
            tapCount++;
            lblTapedCount.Text = String.Format("{0} tap{1}",
                tapCount,
                tapCount == 1 ? "" : "s");
        }
    }
}

Rodando, vamos ficar tocando na imagem:

É uma dica bem simples, mas que ajuda muito!

Caso queira baixar o código utilizado no exemplo, acesse este link.

Quer ver outros artigos sobre Xamarin? Clique aqui.

Espero ter ajudado.

Aquele abraço!