xamarin.forms - 在 Xamarin.Forms 中拖动不规则形状

标签 xamarin.forms mr.gestures

我有一个 Xamarin.Forms 应用程序,我需要在其中拖动不规则形状的控件 (TwinTechForms SvgImageView ),如下所示:

enter image description here

我希望它只响应黑色区域的触摸,而不响应透明(方格)区域的触摸

我尝试使用 MR.Gestures包裹。通过连接平移事件,我可以拖动图像,但当我触摸图像的透明部分时,它也会开始拖动。

我的设置如下:

<mr:ContentView x:Name="mrContentView" Panning="PanningEventHandler" Panned="PannedEventHandler" Background="transparent">
  <ttf:SvgImageView x:Name="svgView" Background="transparent" SvgPath=.../>
</mr:ContentView>

和隐藏代码

private void PanningEventHandler(object sender, PanningEventParameters arg){  
     svgView.TranslateX = arg.IsCancelled ? 0: arg.TotalDistance.X;
     svgView.TranslateY = arg.IsCancelled ? 0: arg.TotalDistance.Y; 
}

private void PannedEventHandler(object sender, PanningEventParameters arg){  
  if (!arg.IsCancelled){
     mrContentView.TranslateX = svgView.TranslateX;
     mrContentView.TranslateY = svgView.TranslateY; 
  }
  svgView.TranslateX = 0;
  svgView.TranslateY = 0;
}

在这段代码隐藏中,我应该如何检查我是否击中了目标对象上的透明点,当发生这种情况时,我如何取消手势,以便该 View 下的另一个 View 可以响应它?在右侧图像中,触摸绿色 O 孔内的红色应开始拖动红色 O

更新:已解决

已接受的答案的建议有效,但并不简单。

我必须 fork 并修改 NGraphics ( github fork ) 和 TwinTechsFormsLib (TTFL, github fork )

在 NGraphics 分支中,我向 SvgReader 添加了一个 XDocument+filter ctor,因此可以使用不同的解析过滤器将相同的 XDocument 传递到不同的 SvgImageView 实例中,从而有效地将原始 SVG 分割为多个 SvgImageView 对象,这些对象可以独立移动,而无需太多很大的内存打击。我必须修复一些画笔继承,以便我的 SVG 能够按预期显示。

TTFL 分支公开了 XDocument+filter ctor,并将特定于平台的 GetPixelColor 添加到渲染器。

然后在我的 Xamarin.Forms 页面中,我可以将原始 SVG 文件加载到多个 SvgImageView 实例中:

List<SvgImageView> LoadSvgImages(string resourceName, int widthRequest = 500, int heightRequest = 500)
{
    var svgImageViews = new List<SvgImageView>();

    var assembly = this.GetType().GetTypeInfo().Assembly;
    Stream stream = assembly.GetManifestResourceStream(resourceName);
    XDocument xdoc = XDocument.Load(stream);

    // only groups that don't have other groups
    List<XElement> leafGroups = xdoc.Descendants()
        .Where(x => x.Name.LocalName == "g" && x.HasElements && !x.Elements().Any(dx => dx.Name.LocalName == "g"))
        .ToList();

    leafGroups.Insert(0, new XElement("nonGroups")); // this one will 
    foreach (XElement leafGroup in leafGroups)
    { 
        var svgImage = new SvgImageView
        {
            HeightRequest = widthRequest,
            WidthRequest = heightRequest,
            HorizontalOptions = LayoutOptions.Start,
            VerticalOptions = LayoutOptions.End,
            StyleId = leafGroup.Attribute("id")?.Value, // for debugging
        };

        // this loads the original SVG as if only there's only one leaf group
        // and its parent groups (to preserve transformations, brushes, opacity etc)
        svgImage.LoadSvgFromXDocument(xdoc, (xe) =>
        {
            bool doRender = xe == leafGroup ||
                            xe.Ancestors().Contains(leafGroup) ||
                            xe.Descendants().Contains(leafGroup); 
            return doRender;
        });

        svgImageViews.Add(svgImage);
    }

    return svgImageViews;
}

然后我将所有 svgImageView 添加到 MR.Gesture <mr:Grid x:Name="movableHost">并将平移和平移事件连接到它。

SvgImageView DragSvgView = null; 点originalPosition = Point.Zero; movingView.Panning += (发送者, pcp) => { //如果我们没有拖动任何东西 - 检查之前加载的 SVG 图像 //如果触摸点处有不透明像素 如果(dragSvgView==null){ DragSvgView = svgImages.FirstOrDefault(si => { var c = si.GetPixelColor(pcp.Touches[0].X - si.TranslationX, pcp.Touches[0].Y - si.TranslationY); 返回 c.A > 0.0001; });

    if (dragSvgView != null)
    {
      // save the original position of this item so we can put it back in case dragging was canceled
      originalPosition = new Point (dragSvgView.TranslationX, dragSvgView.TranslationY);  
    }
  }
  // if we're dragging something - move it along
  if (dragSvgView != null)
  {
    dragSvgView.TranslationX += pcp.DeltaDistance.X;
    dragSvgView.TranslationY += pcp.DeltaDistance.Y;
  }

}

最佳答案

MR.Gestures 和任何底层平台都不会检查 View 中触摸的区域是否透明。监听触摸手势的元素始终是矩形的。所以你必须自己进行 HitTest 。

PanningEventParameters 包含一个 Point[] Touches 以及所有触摸手指的坐标。通过这些坐标,您可以检查它们是否与 SVG 中的任何可见区域匹配。

对样本中的 donut 进行 HitTest 很容易,但测试一般形状则不然(我认为这就是您想要的)。如果你幸运的话,SvgImage 已经支持它了。如果没有,那么您可能会在SVG Rendering Engine中找到如何做到这一点的原理。 , Point-In-Polygon Algorithm — Determining Whether A Point Is Inside A Complex Polygon2D collision detection .

不幸的是,重叠元素是一个问题。当我最初编写 MR.Gestures 时,我尝试使用 Handled 标志来实现它,但我无法让它在所有平台上工作。由于我认为保持一致比使其仅在一个平台上工作更重要,因此我忽略了所有平台上的 Handled ,而是引发所有重叠元素的事件。 (我应该完全删除该标志)

根据您的具体情况,我建议您对多个 SVG 使用这样的结构:

<mr:ContentView x:Name="mrContentView" Panning="PanningEventHandler" Panned="PannedEventHandler" Background="transparent">
  <ttf:SvgImageView x:Name="svgView1" Background="transparent" SvgPath=.../>
  <ttf:SvgImageView x:Name="svgView2" Background="transparent" SvgPath=.../>
  <ttf:SvgImageView x:Name="svgView3" Background="transparent" SvgPath=.../>
</mr:ContentView>

PanningEventHandler 中,您可以检查 Touches 是否在任何 SVG 上,如果是,则哪一个位于顶部。

如果您要执行多个 ContentView,每个内容中仅包含一个 SVG,那么将为每个重叠的矩形元素调用 PanningEventHandler,这不是您想要的.

关于xamarin.forms - 在 Xamarin.Forms 中拖动不规则形状,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41838282/

相关文章:

android - 谁能想出一个自定义渲染器解决方案来更改 Android 中下划线的颜色?

android - 在 Prism 导航页面的工具栏中添加搜索栏

android - 如何在 MasterDetailPage - Xamarin.Forms 中自定义箭头图标、页面图标和页面标题

c# - 在 Xamarin 中覆盖 ViewRenderer

ios - 在 xamarin.forms 中为 IOS 自定义选项卡式页面时遇到问题