我的第一个问题是框视图产生在左上角而不是我指定的设计网格行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);
}
}
}
答案 0 :(得分:1)
我的第一个问题是框视图产生在左上角而不是我指定的设计网格行10和网格列3
正如上面提到的,Grid.Row
,Grid.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模拟器)。
更改了方法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();
}
}