【问题标题】:Xamarin pan gesture recognizer not giving accurate coordinatesXamarin 平移手势识别器未提供准确的坐标
【发布时间】:2016-09-12 20:11:24
【问题描述】:

我正在使用平移手势识别器进行一些测试。设置非常简单,我有一个 20x20 的框视图,并由相对布局设置。平移手势的处理程序

switch (args.StatusType)
            {
                case GestureStatus.Running:
                    box.TranslationX = box.X + args.TotalX;
                    box.TranslationY = box.Y + args.TotalY;
                    break;
                case GestureStatus.Completed:
                    box.TranslationX = 0;
                    box.TranslationY = 0;
                    break;
            }

所以它运行正常,但是当我尝试在屏幕上拖动 boxview 时,它不能正确转换值。当它试图重绘盒子时,它也会产生这种非常讨厌的闪烁效果。有没有更好的方法来做到这一点?有没有办法摆脱闪烁?

【问题讨论】:

  • 你使用的是什么布局?
  • 它是相对布局。
  • 我已经实现了 Xamarin (Adding a Pan Gesture Recognizer) 提供的示例,并且发生了相同的行为。我尝试移动的元素在手势期间闪烁(实际上它从一个位置快速切换到另一个位置,这会导致这种“闪烁”效果)。

标签: xamarin xamarin.forms


【解决方案1】:

好吧,如果是你的盒子有 PanGestureRecognizer,闪烁的效果仅仅是因为 TotalX 和 TotalY 是相对于盒子平移坐标的。

尝试改变

box.TranslationX = box.X + args.TotalX;
box.TranslationY = box.Y + args.TotalY;

通过

box.TranslationX = box.TranslationX + args.TotalX;
box.TranslationY = box.TranslationY + args.TotalY;

它应该可以工作。

编辑:它只涉及 Xamarin Android,它对 UWP 的工作方式不同。

【讨论】:

    【解决方案2】:

    我在 Xamarin 论坛上找到了一个可以帮助您解决问题的实现 (Pinch and Pan gesture recognizers not working on Android project)。这是 Pan 实现,我用我的项目对其进行了测试,发现“闪烁”效果不再存在:

    private double startScale, currentScale, xOffset, yOffset, startX, startY;
    ...
    public void OnPanUpdated(object sender, PanUpdatedEventArgs e)
    {
        switch (e.StatusType)
        {
            case GestureStatus.Started:
                startX = e.TotalX;
                startY = e.TotalY;
                Content.AnchorX = 0;
                Content.AnchorY = 0;
                break;
    
            case GestureStatus.Running:
                var maxTranslationX = Content.Scale * Content.Width - Content.Width;
                Content.TranslationX = Math.Min(0, Math.Max(-maxTranslationX, xOffset + e.TotalX - startX));
    
                var maxTranslationY = Content.Scale * Content.Height - Content.Height;
                Content.TranslationY = Math.Min(0, Math.Max(-maxTranslationY, yOffset + e.TotalY - startY));
                break;
    
            case GestureStatus.Completed:
                xOffset = Content.TranslationX;
                yOffset = Content.TranslationY;
                break;
        }
    }
    

    如果这有帮助,请告诉我。

    【讨论】:

    • 感谢您的回复!我现在正在处理它,但是 maxTranslation 总是返回为 0。看起来它应该类似于 var maxTranslationX = Content.Scale * Screen.Width - Content.Width?你在哪里使用屏幕宽度而不是内容宽度?还是我走错了路?
    【解决方案3】:

    扩展 Julien 的 解决方案,为我解决了这个问题(在 Android 上)...

    PanUpdatedEventArgs 中包含的 Pan Gesture 坐标可以被截取并重新计算,以针对 BoxView 本身的移动进行计数。然后将新的 Args 传递给另一个处理程序:

        public void Handle_PanUpdated(object sender, Xamarin.Forms.PanUpdatedEventArgs e)
        {
            double TranslationX = box.TranslationX;
            double TranslationY = box.TranslationY;
    
            double TotalX_Modified = e.TotalX + TranslationX;
            double TotalY_Modified = e.TotalY + TranslationY;
    
            if (Device.RuntimePlatform == Device.Android)
            { 
                e = new PanUpdatedEventArgs(e.StatusType, e.GestureId, TotalX_Modified, TotalY_Modified);
            }
    
            Handle_PanUpdate_Modified(sender, e);
        }
    

    【讨论】:

      【解决方案4】:

      我终于做到了, 假设我想在屏幕上拖动一个 boxview 而没有任何闪烁或协调精度问题, 我为要在屏幕上拖动的视图创建了一个容器,创建了一个 DragEnd 事件和特定的 EventArgs,当这个事件在主窗体中引发时,我从布局的子列表中删除了这个容器,并重新添加了一个带有 EventArgs 携带的数据的新容器:

      PanContainer.cs :

      public class PanContainer : ContentView
      {
          public delegate void DragEndEventHandler(object sender, DragEventArgs e);
      
          public PanContainer()
          {
              var panGesture = new PanGestureRecognizer();
              panGesture.PanUpdated += OnPanUpdated;
              GestureRecognizers.Add(panGesture);
          }
      
          public event DragEndEventHandler DragEnd;
      
          private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
          {
              switch (e.StatusType)
              {
                  case GestureStatus.Running:
                      Content.TranslationX = e.TotalX;
                      Content.TranslationY = e.TotalY;
                      break;
                  case GestureStatus.Completed:
                      OnDragEnd(new DragEventArgs(Content.TranslationX, Content.TranslationY, Width, Height, X, Y));
                      break;
                  case GestureStatus.Started:
                      break;
                  case GestureStatus.Canceled:
                      break;
                  default:
                      throw new ArgumentOutOfRangeException();
              }
          }
      
          protected virtual void OnDragEnd(DragEventArgs e)
          {
              DragEnd?.Invoke(this, e);
          }
      }
      
      public class DragEventArgs : EventArgs
      {
          public DragEventArgs(double translationX, double translationY, double w, double h, double x, double y)
          {
              ContentTranslationX = translationX;
              ContentTranslationY = translationY;
              Width = w;
              Height = h;
              X = x;
              Y = y;
          }
      
          public double ContentTranslationX { get; set; }
          public double ContentTranslationY { get; set; }
          public double Width { get; set; }
          public double Height { get; set; }
          public double X { get; set; }
          public double Y { get; set; }
      }
      

      在主窗体中:

          private void DraggableOnDragEnd(object sender, DragEventArgs e)
          {
              var bv = new BoxView
              {
                  BackgroundColor = Color.Brown
              };
              var pc = new PanContainer
              {
                  Padding = 4,
                  BackgroundColor = Color.Chartreuse,
                  Content = bv
              };
              absoluteLayout.Children.Add(pc,
                  new Rectangle(e.X + e.ContentTranslationX, e.Y + e.ContentTranslationY, e.Width, e.Height));
              Task.Run(() =>
              {
                  Device.BeginInvokeOnMainThread(() =>
                  {
                      absoluteLayout.Children.Remove(
                          absoluteLayout.Children.First(x => x.GetType() == typeof(PanContainer)));
                      var v = absoluteLayout.Children.First(x => x.GetType() == typeof(PanContainer));
                      ((PanContainer) v).DragEnd += DraggableOnDragEnd;
                  });
              });
          }
      

      enter image description here

      它工作正常,但也许有更好的方法来做到这一点..

      【讨论】:

        猜你喜欢
        • 2012-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多