恢复DraggableView C#Xamarin的位置命令

时间:2017-11-22 14:32:55

标签: c# android ios xaml xamarin

问题是当touchevent结束时,应该调用restore position命令并将其恢复到原始位置。我认为问题在于我的财产改变了事件

第二个问题是,它应该能够锁定到最接近的数字标签,并将它们正确地放在它们上面,但是我不知道如何做到这一点。我想要一些关于如何做到这一点的提示,因为我很无能为力。

网格

<Grid BackgroundColor="White" ColumnSpacing="10" RowSpacing="10">
            <Label Text="Red" FontSize="Medium" HorizontalOptions="Center" />
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>

            <BoxView Color="Black" Grid.Column="1" Grid.RowSpan="1"/>
            <BoxView Color="Gray" Grid.Column="2" Grid.RowSpan="1"/>
            <Label Text="9" Font ="60" Grid.Row="1" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" />
            <Label Text="8" Font ="60" Grid.Row="2" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" />
            <Label Text="7" Font ="60" Grid.Row="3" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" />
            <Label Text="6" Font ="60" Grid.Row="4" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" />
            <Label Text="5" Font ="60" Grid.Row="5" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" />
            <Label Text="4" Font ="60" Grid.Row="6" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" />
            <Label Text="3" Font ="60" Grid.Row="7" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" />
            <Label Text="2" Font ="60" Grid.Row="8" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" />
            <Label Text="1" Font ="60" Grid.Row="9" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" />
            <Label Text="0" Font ="60" Grid.Row="10" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" />

            <local:DraggableView x:Name="dragView" DragMode="LongPress" DragDirection="All" Grid.Row="3" Grid.RowSpan="3" Grid.Column="3" >
                <local:DraggableView.Content>
                    <BoxView x:Name="image" BackgroundColor="Pink" />
                </local:DraggableView.Content>
            </local:DraggableView>
        </Grid>

Crossplatform Code,Xamarin

  public partial class DraggableView : ContentView
        {
            public event EventHandler DragStart = delegate { };
            public event EventHandler DragEnd = delegate { };

        public static readonly BindableProperty DragDirectionProperty = BindableProperty.Create(
        propertyName: "DragDirection",
        returnType: typeof(DragDirectionType),
        declaringType: typeof(DraggableView),
        defaultValue: DragDirectionType.All,
        defaultBindingMode: BindingMode.TwoWay);

    public DragDirectionType DragDirection
    {
        get { return (DragDirectionType)GetValue(DragDirectionProperty); }
        set { SetValue(DragDirectionProperty, value); }
    }


    public static readonly BindableProperty DragModeProperty = BindableProperty.Create(
       propertyName: "DragMode",
       returnType: typeof(DragMode),
       declaringType: typeof(DraggableView),
       defaultValue: DragMode.LongPress,
       defaultBindingMode: BindingMode.TwoWay);

    public DragMode DragMode
    {
        get { return (DragMode)GetValue(DragModeProperty); }
        set { SetValue(DragModeProperty, value); }
    }

    public static readonly BindableProperty IsDraggingProperty = BindableProperty.Create(
      propertyName: "IsDragging",
      returnType: typeof(bool),
      declaringType: typeof(DraggableView),
      defaultValue: false,
      defaultBindingMode: BindingMode.TwoWay);

    public bool IsDragging
    {
        get { return (bool)GetValue(IsDraggingProperty); }
        set { SetValue(IsDraggingProperty, value); }
    }

    public static readonly BindableProperty RestorePositionCommandProperty = BindableProperty.Create(nameof(RestorePositionCommand), typeof(ICommand), typeof(DraggableView), default(ICommand), BindingMode.TwoWay, null, OnRestorePositionCommandPropertyChanged);

    static void OnRestorePositionCommandPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var source = bindable as DraggableView;
        if (source == null)
        {
            return;
        }
        source.OnRestorePositionCommandChanged();
    }

    private void OnRestorePositionCommandChanged()
    {
        OnPropertyChanged("RestorePositionCommand");
    }

    public ICommand RestorePositionCommand
    {
        get
        {
            return (ICommand)GetValue(RestorePositionCommandProperty);
        }
        set
        {
            SetValue(RestorePositionCommandProperty, value);
        }
    }

    public void DragStarted()
    {
        DragStart(this, default(EventArgs));
        IsDragging = true;
    }

    public void DragEnded()
    {
        IsDragging = false;
        DragEnd(this, default(EventArgs));
    }

Android部分代码

public class DraggableViewRenderer : VisualElementRenderer<Xamarin.Forms.View>
    {
        float originalX;
        float originalY;
        float dX;
        float dY;
        bool firstTime = true;
        bool touchedDown = false;
    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
    {
        base.OnElementChanged(e);

        if (e.OldElement != null)
        {
            LongClick -= HandleLongClick;
        }
        if (e.NewElement != null)
        {
            LongClick += HandleLongClick;
            var dragView = Element as DraggableView;
            dragView.RestorePositionCommand = new Command(() =>
            {
                if (!firstTime)
                {
                    SetX(originalX);
                    SetY(originalY);
                }

            });
        }
    }
    private void HandleLongClick(object sender, LongClickEventArgs e)
    {
        var dragView = Element as DraggableView;
        if (firstTime)
        {
            originalX = GetX();
            originalY = GetY();
            firstTime = false;
        }
        dragView.DragStarted();
        touchedDown = true;
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var dragView = Element as DraggableView;
        base.OnElementPropertyChanged(sender, e);

    }
    protected override void OnVisibilityChanged(AView.View changedView, [GeneratedEnum] ViewStates visibility)
    {
        base.OnVisibilityChanged(changedView, visibility);
        if (visibility == ViewStates.Visible)
        {



        }
    }

    // What happens when you toch
    public override bool OnTouchEvent(MotionEvent e)
    {
        float x = e.RawX;
        float y = e.RawY;
        var dragView = Element as DraggableView;
        switch (e.Action)
        {
            case MotionEventActions.Down:
                if (dragView.DragMode == DragMode.Touch)
                {
                    if (!touchedDown)
                    {
                        if (firstTime)
                        {
                            originalX = GetX();
                            originalY = GetY();
                            firstTime = false;
                        }
                        dragView.DragStarted();
                    }
                    touchedDown = true;
                }
                dX = x - this.GetX();
                dY = y - this.GetY();
                break;
            case MotionEventActions.Move:
                if (dragView.DragDirection == DragDirectionType.All || dragView.DragDirection == DragDirectionType.Horizontal)
                {
                    SetX(x - dX);
                }

                if (dragView.DragDirection == DragDirectionType.All || dragView.DragDirection == DragDirectionType.Vertical)
                {
                    SetY(y - dY);
                }
                break;
            case MotionEventActions.Up:
                touchedDown = false;
                dragView.DragEnded();
                break;
            case MotionEventActions.Cancel:
                touchedDown = false;
                break;
        }
        return true;
    }
    public override bool OnInterceptTouchEvent(MotionEvent e)
    {

        BringToFront();
        return true;
    }
}

iOS部件

public class DraggableViewRenderer : VisualElementRenderer<View>
{
    bool longPress = false;
    bool firstTime = true;
    double lastTimeStamp = 0f;
    UIPanGestureRecognizer panGesture;
    CGPoint lastLocation;
    CGPoint originalPosition;
    UIGestureRecognizer.Token panGestureToken;
    void DetectPan()
    {
        var dragView = Element as DraggableView;
        if (longPress || dragView.DragMode == DragMode.Touch)
        {
            if (panGesture.State == UIGestureRecognizerState.Began)
            {
                dragView.DragStarted();
                if (firstTime)
                {
                    originalPosition = Center;
                    firstTime = false;
                }
            }

            CGPoint translation = panGesture.TranslationInView(Superview);
            var currentCenterX = Center.X;
            var currentCenterY = Center.Y;
            if (dragView.DragDirection == DragDirectionType.All || dragView.DragDirection == DragDirectionType.Horizontal)
            {
                currentCenterX = lastLocation.X + translation.X;
            }

            if (dragView.DragDirection == DragDirectionType.All || dragView.DragDirection == DragDirectionType.Vertical)
            {
                currentCenterY = lastLocation.Y + translation.Y;
            }

            Center = new CGPoint(currentCenterX, currentCenterY);

            if (panGesture.State == UIGestureRecognizerState.Ended)
            {
                dragView.DragEnded();
                longPress = false;
            }
        }
    }

    protected override void OnElementChanged(ElementChangedEventArgs<View> e)
    {
        base.OnElementChanged(e);

        if (e.OldElement != null)
        {
            RemoveGestureRecognizer(panGesture);
            panGesture.RemoveTarget(panGestureToken);
        }
        if (e.NewElement != null)
        {
            var dragView = Element as DraggableView;
            panGesture = new UIPanGestureRecognizer();
            panGestureToken = panGesture.AddTarget(DetectPan);
            AddGestureRecognizer(panGesture);


            dragView.RestorePositionCommand = new Command(() =>
            {
                if (!firstTime)
                {

                    Center = originalPosition;
                }
            });

        }

    }
    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var dragView = Element as DraggableView;
        base.OnElementPropertyChanged(sender, e);

    }

    public override void TouchesBegan(NSSet touches, UIEvent evt)
    {
        base.TouchesBegan(touches, evt);
        lastTimeStamp = evt.Timestamp;
        Superview.BringSubviewToFront(this);
        lastLocation = Center;
    }
    public override void TouchesMoved(NSSet touches, UIEvent evt)
    {
        if (evt.Timestamp - lastTimeStamp >= 0.5)
        {
            longPress = true;
        }
        base.TouchesMoved(touches, evt);
    }

}

1 个答案:

答案 0 :(得分:1)

  

问题是当touchevent结束时,应该调用restore position命令并将其恢复到原始位置。我认为问题在于我的财产改变了事件

  1. 如果您需要DraggableView,则应尝试使用ViewRenderer代替VisualElementRenderer<Xamarin.Forms.View>

  2. Renderer不是自定义视图,您不应该尝试覆盖渲染器中的OnTouchEvent,它不会工作,您可以创建自定义视图原生项目,然后在SetNativeControl

    中使用ViewRenderer
    //Create this custom view in your Xamarin.Android project.
    public class DragViewNative:View
    {
    
       public DragViewNative(Context context) : base(context)
       {
    
       }
    
       public override bool OnTouchEvent(MotionEvent e)
       {
           //implement your OnTouchEvent logic here
           ...
       }
     ...
    }
    
    public class DraggableViewRenderer : ViewRenderer
    {
        ...
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
        {
            base.OnElementChanged(e);
            //set native control to be your custom view
            SetNativeControl(new DragViewNative(Xamarin.Forms.Forms.Context));
    
            //other logic here
        }
    }
    
  3. <强>更新 对于RestorePositionCommand未触发的问题:

    由于您未手动调用该命令,因此未触发该命令。由于它是您在控件中定义的自定义ICommand,因此您需要手动调用它来触发它。例如,以下代码将在DragEnded函数中执行:

    //Inside DraggableView.cs
    public void DragEnded()
    {
        IsDragging = false;
    
        //add this line and your command will be triggered
        this.RestorePositionCommand.Execute(null);
        DragEnd(this, default(EventArgs));
    }