c# - 在没有 Storyboard 的情况下以编程方式创建 float 的可拖动按钮(Xamarin)

标签 c# visual-studio drag-and-drop xamarin.ios uibutton

我有一个以编程方式创建的按钮

InfoBtn = _utilProvider.FloatingBtn("My Button"); //Returns Floating UIButton
InfoBtn.Frame = new CGRect((ScreenWidth / 2) - 22.5, ScreenHeight - 65, 55, 55);
View.Add(InfoBtn);

enter image description here

我想以编程方式为其添加拖放功能。问题是,Xamarin 连接的事件不允许我以这种方式使用自定义事件处理程序,例如 UIEvent(直接从 https://www.cocoanetics.com/2010/11/draggable-buttons-labels/ iOS 示例转换而来):

InfoBtn.TouchDragInside += (btn, uievent) =>
{
    var button = (UIButton)btn;
    var e = (UIEvent)uievent; //ERROR: Can not convert type 'System.EventArgs' to 'UIKit.UIEvent'
    // get the touch
    UITouch touch = (UITouch)e.TouchesForView(button).AnyObject;

    // get delta
    CGPoint previousLocation = touch.PreviousLocationInView(button);
    CGPoint location = touch.LocationInView(button);
    nfloat delta_x = location.X - previousLocation.X;
    nfloat delta_y = location.Y - previousLocation.Y;

    // move button
    button.Center = new CGPoint(button.Center.X + delta_x, button.Center.Y + delta_y);
};

根据 Xamarin 的例子在 iOS 中使用 Touch https://developer.xamarin.com/guides/ios/application_fundamentals/touch/ios_touch_walkthrough/他们使用 Storyboard。

创建了一个自定义的 UIViewController

partial class TouchViewController : UIViewController

并通过设置自定义类分配给ViewController

<viewController id="18" sceneMemberID="viewController" customClass="TouchViewController">

如何以编程方式设置 customClass

我还尝试添加一个 UIGestureRecognizer

InfoBtn.AddGestureRecognizer(new MyGestureRecognizer());

partial class MyGestureRecognizer : UIGestureRecognizer
{
    public override void TouchesBegan(NSSet touches, UIEvent evt)
    {
        base.TouchesBegan(touches, evt);

        UITouch touch = touches.AnyObject as UITouch;
        if (touch != null)
        {
            // Touch started
        }
    }

    public override void TouchesCancelled(NSSet touches, UIEvent evt)
    {
        base.TouchesCancelled(touches, evt);
    }

    public override void TouchesEnded(NSSet touches, UIEvent evt)
    {
        base.TouchesEnded(touches, evt);
    }

    public override void TouchesMoved(NSSet touches, UIEvent evt)
    {
        base.TouchesMoved(touches, evt);
        UITouch touch = touches.AnyObject as UITouch;
        if (touch != null)
        {
            // move the shape
        }
    }
}

但它只会进入 TouchesBegan 方法。

在基本上尝试了网上找到的所有教程后,我现在感到很沮丧。

任何帮助将不胜感激

最佳答案

好的,我使用 https://github.com/TwoRedCells/UIDragDropGestureRecognizer-Xamarin.iOS 让它工作了

我对类做了一些修改,这是工作代码:

ViewDidLoad() 中:

InfoBtn = _utilProvider.FloatingBtn("My Button"); //Returns Floating UIButton
var dd = new DragDropGestureRecognizer();
dd.Dragging += (object sender, DragDropEventArgs e) =>
{
    var view = ((DragDropGestureRecognizer)sender).View;

    // Reposition box.
    var x = e.ViewWasAt.X + e.Delta.X;
    var y = e.ViewWasAt.Y + e.Delta.Y;
    view.Center = new CGPoint(x, y);
};
InfoBtn.AddGestureRecognizer(dd);
InfoBtn.TouchUpInside += async (object sender, EventArgs e) =>
{
    //Button On Click
};
InfoBtn.Frame = new CGRect((ScreenWidth / 2) - 22.5, ScreenHeight - 65, 55, 55);
View.Add(InfoBtn);

添加了一个与我的应用程序具有相同命名空间的类 DragDropGestureRecognizer.cs:

using System;
using CGPoint = System.Drawing.PointF;
using Foundation;
using UIKit;

namespace myNS
{
    public class DragDropGestureRecognizer : UIGestureRecognizer
    {
        public DragDropGestureRecognizer()
        {
        }

        public event EventHandler<DragDropEventArgs> Held;
        protected void OnHeld(object sender, DragDropEventArgs e)
        {
            if (Held != null)
                Held(sender, e);
        }

        public event EventHandler<DragDropEventArgs> Dragging;
        protected void OnDragging(object sender, DragDropEventArgs e)
        {
            if (Dragging != null)
                Dragging(sender, e);
        }


        public event EventHandler<DragDropEventArgs> Dropped;
        protected void OnDropped(object sender, DragDropEventArgs e)
        {
            if (Dropped != null)
                Dropped(sender, e);
        }

        public bool DidDrag { get; private set; }
        public CGPoint DownAt { get; private set; }
        public CGPoint DragAt { get; private set; }
        public CGPoint ViewWasAt { get; private set; }
        public CGPoint Delta
        {
            get { return new CGPoint(DragAt.X - DownAt.X, DragAt.Y - DownAt.Y); }
        }
        public bool Active { get { return DidDrag; } }
        public override UIGestureRecognizerState State
        {
            get { return base.State; }
            set { base.State = value; }
        }
        private CGPoint TouchPoint { get { return (CGPoint)LocationInView(View.Superview); } }

        public override bool CanBePreventedByGestureRecognizer(UIGestureRecognizer preventingGestureRecognizer)
        {
            return false;
        }

        public override void TouchesBegan(NSSet touches, UIEvent evt)
        {
            base.TouchesBegan(touches, evt);

            if (NumberOfTouches > 1)
            {
                State = UIGestureRecognizerState.Failed;
                return;
            }

            OnHeld(this, new DragDropEventArgs(default(UIGestureRecognizerState), DragAt, Delta, ViewWasAt));

            DownAt = TouchPoint;
            ViewWasAt = (CGPoint)View.Center;
            State = UIGestureRecognizerState.Possible;
        }

        public override void TouchesEnded(NSSet touches, UIEvent evt)
        {
            base.TouchesEnded(touches, evt);

            if (DidDrag)
            {
                State = UIGestureRecognizerState.Recognized;
                OnDropped(this, new DragDropEventArgs(State, DragAt, Delta, ViewWasAt));
            }
            else
                State = UIGestureRecognizerState.Failed;
        }

        public override void TouchesCancelled(NSSet touches, UIEvent evt)
        {
            base.TouchesCancelled(touches, evt);
            State = UIGestureRecognizerState.Failed;
        }

        public override void TouchesMoved(NSSet touches, UIEvent evt)
        {
            base.TouchesMoved(touches, evt);
            if (State == UIGestureRecognizerState.Failed)
                return;

            DragAt = TouchPoint;
            if (Distance(DownAt, DragAt) > 30 || DidDrag) //Won't move until dragged further than 30px
            {
                DidDrag = true;
                OnDragging(this, new DragDropEventArgs(State, DragAt, Delta, ViewWasAt));
                State = UIGestureRecognizerState.Changed;
            }
        }

        public override void Reset()
        {
            base.Reset();

            State = UIGestureRecognizerState.Possible;
            DownAt = CGPoint.Empty;
            DragAt = CGPoint.Empty;
            DidDrag = false;
        }

        private float Distance(CGPoint point1, CGPoint point2)
        {
            var dx = point1.X - point2.X;
            var dy = point1.Y - point2.Y;
            return (float)Math.Sqrt(dx * dx + dy * dy);
        }
    }

    public class DragDropEventArgs : EventArgs
    {
        public DragDropEventArgs(UIGestureRecognizerState state, CGPoint point, CGPoint delta, CGPoint viewWasAt)
        {
            State = state;
            Point = point;
            Delta = delta;
            ViewWasAt = viewWasAt;
        }

        public UIGestureRecognizerState State { get; private set; }
        public CGPoint Point { get; private set; }
        public CGPoint Delta { get; private set; }
        public CGPoint ViewWasAt { get; private set; }
    }
}

感谢 Yvan Rodrigues - TwoRedCells


Android 等效项:

private int _xPad, _yPad, _xDelta, _yDelta;
prviate bool _moved;

InfoBtn.Touch += (v, me) => //InfoBtn is a button within a frameLayout
{
    int X = (int)me.Event.RawX;
    int Y = (int)me.Event.RawY;
    switch (me.Event.Action & MotionEventActions.Mask)
    {
        case MotionEventActions.Down:
            _xPad = frameLayout.PaddingLeft;
            _yPad = frameLayout.PaddingTop;
            _xDelta = X;
            _yDelta = Y;
            _moved = false;
            break;
        case MotionEventActions.Up:
            if (!_moved)
            {
                //On Button Click
            }
            break;
        case MotionEventActions.PointerDown:
            break;
        case MotionEventActions.PointerUp:
            break;
        case MotionEventActions.Move:
            var _x = X - _xDelta;
            var _y = Y - _yDelta;
            _moved = _moved || Math.Abs(_x) > 100 || Math.Abs(_y) > 100; //100px
            if (_moved)
            {
                var padleft = _x - _xPad;
                padleft = padleft + InfoBtn.Width > Resources.DisplayMetrics.WidthPixels ? Resources.DisplayMetrics.WidthPixels - InfoBtn.Width : padleft;
                var padtop = _y - _yPad;
                padtop = padtop + InfoBtn.Height > Resources.DisplayMetrics.HeightPixels ? Resources.DisplayMetrics.HeightPixels - InfoBtn.Height : padtop;
                frameLayout.SetPadding(0, 0, padleft, padtop);
            }
            break;
    }
    frameLayout.Invalidate();
};

关于c# - 在没有 Storyboard 的情况下以编程方式创建 float 的可拖动按钮(Xamarin),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41821364/

相关文章:

c# - 是否可以在运行时更改 WebReference 的属性?

c# - 错误 : typeInitializationException

c# - 使用 Visual Studio 在代码中查找可等待的方法

qt+pyqt 发出两次丢弃的 URL

javascript - jQuery 中如何在拖动子级时触发父级拖动事件?

c# - 提前访问 ASP.NET MVC 中的异步操作结果

c# - 具有多个 DictionaryEntry 的 foreach

visual-studio - Visual Studio 2017 调试 vbscript

c# - 我无法在调用的委托(delegate)中捕获异常

python - 如何让 Tkinter textarea 接受在 Python 3.X 上删除外部文件?