Xamarin Formsのマウスイベントが少ない話の続き

yarukizero.hatenablog.jp

前回書かなかったレンダラの実装。今回は実際に書いたコードを張り付けるだけの簡単なお仕事なので前回の話と微妙に違う。

まず共通部分。

public interface IClickableView {
	bool CallClick();
	bool CallPointerDown();
	bool CallPointerUp();
}

public class ClickableView : global::Xamarin.Forms.BoxView, IClickableView {
	public static readonly BindableProperty IsPointerDownProperty =
		BindableProperty.Create(nameof(IsPointerDown), typeof(bool), typeof(ClickableView), default(bool),
			propertyChanged: (bindable, oldValue, newValue) => ((ClickableView)bindable).IsPointerDown = (bool)newValue);

	public bool IsPointerDown {
		get { return (bool)GetValue(IsPointerDownProperty); }
		set { SetValue(IsPointerDownProperty, value); }
	}
	
	public static readonly BindableProperty ForegroundColorProperty =
		BindableProperty.Create(nameof(ForegroundColor), typeof(Color), typeof(ClickableView), default(Color),
			propertyChanged: (bindable, oldValue, newValue) =>	((ClickableView)bindable).ForegroundColor = (Color)newValue);

	public Color ForegroundColor {
		get { return (Color)GetValue(ForegroundColorProperty); }
		set { SetValue(ForegroundColorProperty, value); }
	}
	
	public event EventHandler<EventArgs> Clicked;
	public event EventHandler<EventArgs> PointerDown;
	public event EventHandler<EventArgs> PointerUp;

	bool IClickableView.CallClick() {
		Clicked?.Invoke(this, EventArgs.Empty);

		return Clicked != null;
	}

	bool IClickableView.CallPointerDown() {
		IsPointerDown = true;
		PointerDown?.Invoke(this, EventArgs.Empty);

		return PointerDown != null;
	}
	bool IClickableView.CallPointerUp() {
		IsPointerDown = false;
		PointerUp?.Invoke(this, EventArgs.Empty);

		return PointerUp != null;
	}
}

プラットフォーム個別実装でandroid

public class ClickableViewHelper {
    public class Helper<T> where T : Android.Views.View, IVisualElementRenderer {
        private IClickableView target;

        public Helper(T renderer) {
            renderer.ElementChanged += (s, e) =>
            {
                if (e.NewElement == null) {
                    // 後片付け必要?
                    return;
                } else { 
                    target = e.NewElement as IClickableView;
                    System.Diagnostics.Debug.Assert(target != null);
                    renderer.Touch += (snder, ev) =>
                    {
                        var pointerIndex = ((int)ev.Event.Action) >> ((int)MotionEventActions.PointerIndexShift) & ((int)MotionEventActions.PointerIndexMask);
                        switch (ev.Event.Action & MotionEventActions.Mask) {
                            case MotionEventActions.Down:
                                ev.Handled = target.CallPointerDown();
                                break;
                            case MotionEventActions.Up:
                                ev.Handled = target.CallPointerUp();
                                if(new Rectangle(
                                    0, 0,
                                    renderer.Width, renderer.Height).Contains(
                                        ev.Event.GetX(), ev.Event.GetY())) {

                                    target.CallClick();
                                }
                                break;

                            case MotionEventActions.Cancel:
                                break;

                        }
                    };
                }
            };
        }
    }

    public static Helper<T> Create<T>(T renderer) where T : Android.Views.View, IVisualElementRenderer {
        return new Helper<T>(renderer);
    }
}

public class ClickableViewRenderer : BoxRenderer {
    private object helper;
    public ClickableViewRenderer() {
        helper = ClickableViewHelper.Create(this);
    }
}

UWPはこちら。

public class ClickableViewHelper {
    public class Helper<T> where T : Windows.UI.Xaml.Controls.Panel, IVisualElementRenderer {
        private IClickableView target;

        public Helper(T renderer) {
            renderer.ElementChanged += (s, e) =>
            {
                if (e.NewElement == null) {
                    // 後片付け必要?

                    return;
                } else {
                    target = e.NewElement as IClickableView;
                    System.Diagnostics.Debug.Assert(target != null);

                    renderer.PointerPressed += (sender, ev) =>
                    {
                        renderer.CapturePointer(ev.Pointer);
                        target?.CallPointerDown();
                    };
                    renderer.PointerReleased += (sender, ev) =>
                    {
                        renderer.ReleasePointerCapture(ev.Pointer);
                        if (target != null) {
                            target.CallPointerUp();

                            var pt = ev.GetCurrentPoint(renderer);
                            if ((0 <= pt.Position.X)
                                && (pt.Position.X <= renderer.ActualWidth)
                                && (0 <= pt.Position.Y)
                                && (pt.Position.Y <= renderer.ActualHeight)) {

                                target.CallClick();
                            }
                        }
                    };
                }
            };
        }
    }

    public static Helper<T> Create<T>(T renderer) where T : Windows.UI.Xaml.Controls.Panel, IVisualElementRenderer {
        return new Helper<T>(renderer);
    }
}

public class ClickableViewRenderer : BoxViewRenderer {
    private object helper;
    public ClickableViewRenderer() {
        helper = ClickableViewHelper.Create(this);
    }
}