Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Pinch and Pan gesture recognizers not working on Android project

I'm using a custom ContentView object with three different gesture recognizers: TapGestureRecognizer, PinchGestureRecognizer and PanGestureRecognizer (using Xamarin.Forms 2.0.1).

Here's the custom ContentView constructor where I defined the gesture recognizers...

// Constructor public ImageContainer() { TapGestureRecognizer tapGesture = new TapGestureRecognizer(); PinchGestureRecognizer pinchGesture = new PinchGestureRecognizer(); PanGestureRecognizer panGesture = new PanGestureRecognizer(); tapGesture.NumberOfTapsRequired = 2; tapGesture.Tapped += OnTapEvent; // event handler not getting called on Android pinchGesture.PinchUpdated += OnPinchUpdated; panGesture.PanUpdated += OnPanUpdated; // event handler not getting called on Android GestureRecognizers.Add(tapGesture); GestureRecognizers.Add(pinchGesture); GestureRecognizers.Add(panGesture); }

All three gesture recognizers work as expected on Windows Phone 8.1 and iOS.
The issue I'm having is that on Android the tap and pan gesture recognizers are not working, their event handlers are not even getting called and I'm not getting any errors.
Any ideas?

Thank you.

Best Answer

Answers

  • rarenivarrarenivar USMember ✭✭

    I've been trying to figure out what's causing this and it seems to be somehow related to the PinchGestureRecognizer somehow overwriting the other two gesture recognizers event handlers. When I don't add the PinchGestureRecognizer to my custom ContentView, the other two work as expected.

  • Ramiro, I am experiencing the exact same issue. Hopefully it gets escalated as it's a pretty major flaw in the API. Thanks for logging the bug.

  • DavideZordanDavideZordan GBUniversity ✭✭

    Same issue here. If you add multiple GestureRecognizers only PinchGestureRecognizer works on Android.
    Thanks for logging it, Ramiro.

  • MichaelRumplerMichaelRumpler ATMember ✭✭✭✭✭

    You can use MR.Gestures which handles all the gestures at the same time. More info on http://www.mrgestures.com/ .
    The sample project available at https://github.com/MichaelRumpler/GestureSample demonstrated panning and pinching at the same time.

  • WarinthornSWarinthornS THMember

    thank you @RichardHorky pan and pinch very good ^^

  • RanjithkumarRamakrishnanRanjithkumarRamakrishnan USMember ✭✭
    edited September 2016

    @MichaelRumpler your library is open source?

    Your answer like advertisment

  • MichaelRumplerMichaelRumpler ATMember ✭✭✭✭✭

    @RanjithkumarRamakrishnan It is not open source. It costs € 10,- which is much less than you'd need to code it yourself. All this information is available at the web site I linked above.

  • just what I needed @RichardHorky thanks 4 sharing

  • JordanMaxJordanMax USMember ✭✭

    @RichardHorky said:
    This is my solution of this problem - own renderer in case of Android

    Portable:

    public class PinchToZoomContainer : ContentView
    {
        private double startScale, currentScale, xOffset, yOffset, startX, startY;
    
        public PinchToZoomContainer()
        {
            if (Device.OS != TargetPlatform.Android)
            {
                var pinchGesture = new PinchGestureRecognizer();
                pinchGesture.PinchUpdated += OnPinchUpdated;
                GestureRecognizers.Add(pinchGesture);
    
                var panGesture = new PanGestureRecognizer();
                panGesture.PanUpdated += OnPanUpdated;
                GestureRecognizers.Add(panGesture);
            }
        }
    
        public void OnPanUpdated(object sender, PanUpdatedEventArgs e)
        {
            switch (e.StatusType)
            {
                case GestureStatus.Started:
                    startX = e.TotalX;
                    startY = e.TotalY;
                    Content.AnchorX = 0;
                    Content.AnchorY = 0;
    
                    break;
    
                case GestureStatus.Running:
                    var maxTranslationX = Content.Scale * Content.Width - Content.Width;
                    Content.TranslationX = Math.Min(0, Math.Max(-maxTranslationX, xOffset + e.TotalX - startX));
    
                    var maxTranslationY = Content.Scale * Content.Height - Content.Height;
                    Content.TranslationY = Math.Min(0, Math.Max(-maxTranslationY, yOffset + e.TotalY - startY));
    
                    break;
    
                case GestureStatus.Completed:
                    xOffset = Content.TranslationX;
                    yOffset = Content.TranslationY;
    
                    break;
            }
        }
    
        public void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
        {
            switch (e.Status)
            {
                case GestureStatus.Started:
                    // Store the current scale factor applied to the wrapped user interface element,
                    // and zero the components for the center point of the translate transform.
                    startScale = Content.Scale;
                    Content.AnchorX = 0;
                    Content.AnchorY = 0;
    
                    break;
    
                case GestureStatus.Running:
                    // Calculate the scale factor to be applied.
                    currentScale += (e.Scale - 1) * startScale;
                    currentScale = Math.Max(1, currentScale);
    
                    // The ScaleOrigin is in relative coordinates to the wrapped user interface element,
                    // so get the X pixel coordinate.
                    double renderedX = Content.X + xOffset;
                    double deltaX = renderedX / Width;
                    double deltaWidth = Width / (Content.Width * startScale);
                    double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
    
                    // The ScaleOrigin is in relative coordinates to the wrapped user interface element,
                    // so get the Y pixel coordinate.
                    double renderedY = Content.Y + yOffset;
                    double deltaY = renderedY / Height;
                    double deltaHeight = Height / (Content.Height * startScale);
                    double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
    
                    // Calculate the transformed element pixel coordinates.
                    double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
                    double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);
    
                    // Apply translation based on the change in origin.
                    Content.TranslationX = Math.Min(0, Math.Max(targetX, -Content.Width * (currentScale - 1)));
                    Content.TranslationY = Math.Min(0, Math.Max(targetY, -Content.Height * (currentScale - 1)));
    
                    // Apply scale factor.
                    Content.Scale = currentScale;
    
                    break;
    
                case GestureStatus.Completed:
                    // Store the translation delta's of the wrapped user interface element.
                    xOffset = Content.TranslationX;
                    yOffset = Content.TranslationY;
    
                    break;
            }
        }
    }
    

    I used this solution and it appears to be working pretty well. The only issue is, and perhaps this is because I'm not fully understanding and didn't design it correctly, but when it's zoomed in, It's hard to zoom out. I almost feel like once you're zoomed in, the OnPinch method doesn't get called. Any thoughts?

  • ATIFSHAHZADATIFSHAHZAD USMember ✭✭

    Awsome, Please guide me how can we zoom in and zoom out on finger double tap.

    Thanks

Sign In or Register to comment.