Bug 26738 - MotionEvent's GetX and GetY are wrong for Nexus 9 and Nexus 7
Summary: MotionEvent's GetX and GetY are wrong for Nexus 9 and Nexus 7
Status: RESOLVED ANSWERED
Alias: None
Product: Android
Classification: Xamarin
Component: Mono runtime / AOT Compiler ()
Version: 5.1
Hardware: Other Windows
: Normal normal
Target Milestone: ---
Assignee: Jonathan Pryor
URL:
Depends on:
Blocks:
 
Reported: 2015-02-04 12:31 UTC by Jeremy Sheeley
Modified: 2015-03-06 10:33 UTC (History)
2 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Reproduction solution. (315.11 KB, application/x-zip-compressed)
2015-02-04 12:33 UTC, Jeremy Sheeley
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED ANSWERED

Description Jeremy Sheeley 2015-02-04 12:31:09 UTC
Posted on the forum here:

http://forums.xamarin.com/discussion/32213/android-strangeness-with-gesturerecognizer-and-nexus-9-and-nexus-7#latest

The most succinct way of stating it is that GetX and GetY for MotionEvent are too large when run on Nexus 9 and Nexus 7. On other devices, the values are fine.  Looking at the XResolution and YResolution, the values on the Nexus 9 are smaller than I would expect. (1.15 and 2.0, respectively). On a Samsung Galaxy Tab 3, the resolution values are something like 3 and 5.

My example code is written in the context of Xamarin.Forms renderer.

Here's the portable page that moves a box in response to a single tap.

    public class CustomView : AbsoluteLayout
    {
        BoxView bv;
        public CustomView()
        {
            this.HorizontalOptions = LayoutOptions.FillAndExpand;
            this.VerticalOptions = LayoutOptions.FillAndExpand;
            this.BackgroundColor = Color.Blue;
            bv = new BoxView();
            bv.Color = Color.Pink;
            Children.Add(bv);
            AbsoluteLayout.SetLayoutBounds(bv, new Rectangle(0, 0, 40, 40));
        }
        public void MoveBox(double x, double y)
        {
            AbsoluteLayout.SetLayoutBounds(bv, new Rectangle(x, y, 40, 40));
        }
    }

    public class App : Application
    {
        public App()
        {
            // The root page of your application
            MainPage = new ContentPage
            {
                Content = new StackLayout
                {
                    VerticalOptions = LayoutOptions.FillAndExpand,
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    Children = {
                        new CustomView {
                            }
                    }
                }
            };
        }

        protected override void OnStart()
        {
            // Handle when your app starts
        }

        protected override void OnSleep()
        {
            // Handle when your app sleeps
        }

        protected override void OnResume()
        {
            // Handle when your app resumes
        }
    }





-------

And now here's the renderer, that has the GestureRecognizer.

using System;

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

using Xamarin.Forms.Platform.Android;

[assembly: Xamarin.Forms.ExportRenderer(typeof(Android_GestureRecognizer.CustomView), typeof(Android_GestureRecognizer.Droid.CustomViewRenderer))]

namespace Android_GestureRecognizer.Droid
{

    public class CustomViewRenderer : VisualElementRenderer<Android_GestureRecognizer.CustomView>, GestureDetector.IOnGestureListener
    {
        private GestureDetector _g;

        public CustomViewRenderer()
        {
            _g = new GestureDetector(this);
        }

        private CustomView theview // TODO
        {
            get
            {
                return Element;
            }
        }
        public bool OnSingleTapUp(MotionEvent ev)
        {
            //Tell the view to move the BoxView. 
            // This is where the values are larger than they should be.
            theview.MoveBox(ev.GetX(), ev.GetY());
            return true;
        }

        public void OnLongPress(MotionEvent ev)
        {
        }

        public void OnShowPress(MotionEvent ev)
        {
        }

        public bool OnFling(MotionEvent ev1, MotionEvent ev2, float vx, float vy)
        {
            return false;
        }

        public bool OnDown(MotionEvent ev)
        {
            return false;
        }

        public bool OnScroll(MotionEvent ev1, MotionEvent ev2, float dx, float dy)
        {
            return false;
        }

        public override bool OnTouchEvent(MotionEvent e)
        {
            bool b = _g.OnTouchEvent(e);
            if (b)
            {
                return true;
            }
            else
            {
                return base.OnTouchEvent(e);
            }
        }
    }


    [Activity(Label = "Android_GestureRecognizer", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());
        }
    }
}
Comment 1 Jeremy Sheeley 2015-02-04 12:33:04 UTC
Created attachment 9643 [details]
Reproduction solution.
Comment 2 Jonathan Pryor 2015-03-02 15:00:34 UTC
Xamarin.Android is a direct binding over Android. It doesn't have a 'copy' of Android; it invokes into Android, and thus *should* exhibit the same behavioral properties as Java code [0].

As such, "wrong values" are not necessarily a bug in Xamarin.Android, IF Java code behaves the same way.

To know if this is actually a Xamarin bug, we'd need to create an equivalent Java app with the same semantics (same targetSdkVersion, etc.), and see if the values differ.

I suspect the more probable cause is that Android's behavior varies across devices, and you're observing yet another form of Android fragmentation.

[0]: Modulo some exceptions, such as the existence of GREFs...
Comment 3 Jeremy Sheeley 2015-03-05 15:35:57 UTC
It's not actually sending the wrong values. The core cause of this is. The size of the renderer does not match the size of the view. Scaling the touch coordinates before sending them to the pcl fixed the issue.

double touch_x = ev.GetX();
double touch_y = ev.GetY();

if (this.Width != tab.Width)
    touch_x = touch_x * (theview.Width / this.Width);

if (this.Height != tab.Height)
    touch_y = touch_y * (theview.Height / this.Height);

theview.MoveBox(touch_x, touch_y);
Comment 4 Jeremy Sheeley 2015-03-05 15:51:30 UTC
info provided.
Comment 5 Jason Smith [MSFT] 2015-03-05 16:07:02 UTC
Xamarin.Forms exports values to the user as dips. 

Xamarin.Forms.Platform.Android.ContextExtensions provides methods to convert between the two.
Comment 6 Jeremy Sheeley 2015-03-06 10:33:20 UTC
Thanks for pointing me to that. Do I also need to adjust the pixels for ios or winphone?