stepheaw
stepheaw

Reputation: 1713

How can I interact with a xamarin forms image in Android?

I have an image in Xamarin Forms. I want to use the native android features to interact with this image. For example, when the image is tapped, I want to know the x,y coordinates of where the image was tapped. I can use Android ImageView but I'm not sure how to cast the Xamarin Forms image to Android ImageView

[assembly: ExportRenderer(typeof(Image), typeof(FloorplanImageRenderer))]
namespace EmployeeApp.Droid.Platform
{

  public class FloorplanImageRenderer : ImageRenderer
  {
        protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
        {
            if (Control == null)
            {

                var imageView = (ImageView)e.NewElement; // This is not right

            }

            base.OnElementChanged(e);
        }
   }
}

Upvotes: 0

Views: 692

Answers (1)

Grace Feng
Grace Feng

Reputation: 16652

But the Control is null....

No, it shouldn't be null. Back to your question, I think first of all, you will need to attach a touch event to the image control in PCL and create a property to hold the coordinate when image get touched. And I think here in your code:

[assembly: ExportRenderer(typeof(Image), typeof(FloorplanImageRenderer))]

I think the Image here should be your custom image control which inherits from Image in PCL.

Create a interface for touch event:

public interface IFloorplanImageController
{
    void SendTouched();
}

Create a custom control for image:

 public class FloorplanImage : Image, IFloorplanImageController
 {
     public event EventHandler Touched;

     public void SendTouched()
     {
         Touched?.Invoke(this, EventArgs.Empty);
     }

     public Tuple<float, float> TouchedCoordinate
     {
         get { return (Tuple<float, float>)GetValue(TouchedCoordinateProperty); }
         set { SetValue(TouchedCoordinateProperty, value); }
     }

     public static readonly BindableProperty TouchedCoordinateProperty =
         BindableProperty.Create(
             propertyName: "TouchedCoordinate",
             returnType: typeof(Tuple<float, float>),
             declaringType: typeof(FloorplanImage),
             defaultValue: new Tuple<float, float>(0, 0),
             propertyChanged: OnPropertyChanged);

     public static void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue)
     {
     }
 }

Implement the custom renderer:

[assembly: ExportRenderer(typeof(FloorplanImage), typeof(FloorplanImageRenderer))]

namespace EmployeeApp.Droid.Platform
{
    public class FloorplanImageRenderer : ImageRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement != null)
            {
                if (Control != null)
                {
                    Control.Clickable = true;
                    Control.SetOnTouchListener(ImageTouchListener.Instance.Value);
                    Control.SetTag(Control.Id, new JavaObjectWrapper<FloorplanImage> { Obj = Element as FloorplanImage });
                }
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (Control != null)
                {
                    Control.SetOnTouchListener(null);
                }
            }

            base.Dispose(disposing);
        }

        private class ImageTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
        {
            public static readonly Lazy<ImageTouchListener> Instance = new Lazy<ImageTouchListener>(
                () => new ImageTouchListener());

            public bool OnTouch(Android.Views.View v, MotionEvent e)
            {
                var obj = v.GetTag(v.Id) as JavaObjectWrapper<FloorplanImage>;
                var element = obj.Obj;
                var controller = element as IFloorplanImageController;
                if (e.Action == Android.Views.MotionEventActions.Down)
                {
                    var x = e.GetX();
                    var y = e.GetY();
                    element.TouchedCoordinate = new Tuple<float, float>(x, y);
                    controller?.SendTouched();
                }
                else if (e.Action == Android.Views.MotionEventActions.Up)
                {
                }
                return false;
            }
        }
    }

    public class JavaObjectWrapper<T> : Java.Lang.Object
    {
        public T Obj { get; set; }
    }
}

Use this control like this:

<local:FloorplanImage HeightRequest="300" x:Name="image" WidthRequest="300"
                      Aspect="AspectFit"  Touched="image_Touched" />

code behind:

private void image_Touched(object sender, EventArgs e)
{
    var cor = image.TouchedCoordinate;
}

Upvotes: 1

Related Questions