Draggable Boxview不会更新Xamarin

时间:2017-11-20 09:53:22

标签: c# android ios xamarin

我的第一个问题是框视图产生在左上角而不是我指定的设计网格行10和网格列3

第二个问题在于可拖动视图,在代码本地拖动视图的第一部分,它正确地调用了触摸事件,但是它可能没有在GUI中更新?

<ContentPage>
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness">
            <On Platform="iOS, Android" Value="0,40,0,0" />
        </OnPlatform>
    </ContentPage.Padding>
    <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" >
                <local:DraggableView.Content>
                    <BoxView x:Name="image" BackgroundColor="Pink" Grid.Row="10" Grid.Column="3"/>
                </local:DraggableView.Content>
        </local:DraggableView>
    </Grid>

助手

public enum DragDirectionType
{
    All,
    Vertical,
    Horizontal
}
public enum DragMode
{
    Touch,
    LongPress
}

DraggableView

 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 void DragStarted()
        {
            DragStart(this, default(EventArgs));
            IsDragging = true;
        }

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

    }
}

这是代码的android部分

[assembly: ExportRenderer(typeof(DraggableView), typeof(DraggableViewRenderer))]

namespace BabakusXamarin.Droid
{
    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;
            }
        }

        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)
            {



            }
        }
        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 (touchedDown)
                    {
                        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 base.OnTouchEvent(e);
        }

        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);
        }

    }
}

2 个答案:

答案 0 :(得分:1)

  

我的第一个问题是框视图产生在左上角而不是我指定的设计网格行10和网格列3

正如上面提到的,Grid.RowGrid.Column从0开始,您应该在DraggableView而不是BoxView上设置它们。

修改您的代码,如下所示

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

第二个问题在于可拖动视图,在代码本地拖动视图的第一部分,它正确地调用了触摸事件,但是它可能没有在GUI中更新?

您测试了哪个平台? Android还是iOS?

它非常适合我(iOS模拟器)。

enter image description here

更新

更改了方法OnTouchEvent的逻辑后,它按预期工作。

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 (touchedDown)
                //{
                    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;
    }

答案 1 :(得分:1)

在过去,我做了类似的控制。我在Android上遇到的部分问题是我还必须合并屏幕密度才能正确拖动。这是我的实施。

控制:

public class DraggableContentView : ContentView
{
    public event EventHandler TouchEnded;
    public event EventHandler TouchesBegan;
    public event EventHandler PositionChanged;

    public void InvokeTouchBegan()
    {
        var parentLayout = Parent as Layout<View>;
        parentLayout?.RaiseChild(this);
        TouchesBegan?.Invoke(this, EventArgs.Empty);
    }

    public void InvokePositionChanged()
    {
        PositionChanged?.Invoke(this, EventArgs.Empty);
    }

    public void InvokeTouchEnded()
    {
        TouchEnded?.Invoke(this, EventArgs.Empty);
    }
}

Android渲染器:

public class DraggableContentViewRenderer : ViewRenderer<DraggableContentView, Android.Views.View>
{
    private float _density, _downX, _downY;

    public DraggableContentViewRenderer()
    {
        _density = Android.App.Application.Context.Resources.DisplayMetrics.Density;
    }

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

        if (Element != null)
        {
            if (Control == null)
            {
                SetNativeControl(new Android.Views.View(Xamarin.Forms.Forms.Context));
            }
        }
    }

    public override bool DispatchTouchEvent(MotionEvent e)
    {
        if (!Element.IsEnabled)
            return false;

        switch (e.Action)
        {
            case MotionEventActions.Down:
                {
                    _downX = e.GetX();
                    _downY = e.GetY();
                    Element.InvokeTouchBegan();
                    break;
                }
            case MotionEventActions.Move:
                {
                    var x = e.GetX();
                    var y = e.GetY();
                    var dx = (x - _downX) / _density;
                    var dy = (y - _downY) / _density;
                    Element.TranslationX += dx;
                    Element.TranslationY += dy;
                    Element.InvokePositionChanged();
                    break;
                }
            case MotionEventActions.Up:
            case MotionEventActions.Cancel:
                {
                    Element.InvokeTouchEnded();
                    break;
                }
            default:
                break;
        }
        return true;
    }
}

iOS渲染器:

public class DraggableContentViewRenderer : ViewRenderer<DraggableContentView, UIView>
{
    private CGPoint _offsetLocation;

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

        if (Element != null)
        {
            if (Control == null)
            {
                SetNativeControl(new UIView());
            }
        }
    }

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

        var t = touches.ToArray<UITouch>();
        if (t.Length != 1) 
            return;

        var loc = t[0].LocationInView(this);

        var touchedView = HitTest(loc, evt);
        if (touchedView == null) 
            return;

        _offsetLocation = new CGPoint(loc.X - touchedView.Frame.X, loc.Y - touchedView.Frame.Y);

        Element.InvokeTouchBegan();
    }

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

        var newLoc = ((UITouch)touches.First()).LocationInView(this);
        Element.TranslationX += newLoc.X - _offsetLocation.X;
        Element.TranslationY += newLoc.Y - _offsetLocation.Y;
        Element.InvokePositionChanged();
    }

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

        Element.InvokeTouchEnded();
    }
}