将水平滚动与WebView结合使用时的滚动优先级 [英] Scrolling priority when combining horizontal scrolling with WebView

查看:199
本文介绍了将水平滚动与WebView结合使用时的滚动优先级的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在水平滚动PageView中有一个垂直滚动 WebView .像这样:

I have a vertically scrolling WebView inside a horizontally scrolling PageView. Something like this:

PageView.builder(
  itemCount: 5,
  itemBuilder: (context, index) {
    return WebView(
      initialUrl: 'https://flutter.dev/docs',
      gestureRecognizers: [
        Factory(() => VerticalDragGestureRecognizer()),
      ].toSet(),
    );
  },
);

在Flutter的先前稳定版本(1.5.4)中,此操作按预期工作-垂直滚动将在WebView中移动内容,水平滚动将在PageView中移动.

With the previous stable version of Flutter (1.5.4), this worked as expected - scrolling vertically would move the content inside the WebView and scrolling horizontally would move the PageView.

在升级到Flutter v1.7.8+hotfix.3后,此问题中断了.现在,即使手势几乎完全是垂直的,水平滚动似乎总是可以取胜的.如果页面完全垂直滚动,则仅在手势停止后(即,手势停止后我停止触摸屏幕)–手势进行时就不会进行垂直滚动.

This broke after upgrading to Flutter v1.7.8+hotfix.3. Now horizontal scrolling seems always to win, even if the gesture is very clearly almost entirely vertical. If the page gets scrolled vertically at all, it is only after the gesture stops (i.e., when I stop touching the screen after a gesture) - there is no vertical scrolling while the gesture is happening.

gestureRecognizers中添加和删除VerticalDragGestureRecognizer现在不起作用-程序以任何一种方式工作,就好像识别器不在列表中(尽管并不是gestureRecognizers会被完全忽略,因为添加EagerGestureRecognizer确实有效果).

Adding and removing VerticalDragGestureRecognizer from gestureRecognizers have no effect now - either way the program works as if the recognizer was not on the list (although it's not that gestureRecognizers are completely ignored because adding EagerGestureRecognizer DOES have an effect).

这是手势竞技场的调试输出(请记住,我试图将手势保持尽可能垂直,但是即使手指稍微向两侧移动也足以使HorizontalDragGestureRecognizer获胜,尽管我也一直在垂直移动):

Here is the debug output of the gesture arena (keep in mind that I was trying to keep my gesture as vertical as possible, but even a slight finger movement to the sides was enough for the HorizontalDragGestureRecognizer to win, even though I also was moving vertically the entire time):

I/flutter (30125): Gesture arena 14   ❙ ★ Opening new gesture arena.
I/flutter (30125): Gesture arena 14   ❙ Adding: Instance of '_CombiningGestureArenaMember'
I/flutter (30125): Gesture arena 14   ❙ Adding: LongPressGestureRecognizer#9cad1(debugOwner: GestureDetector, state: ready)
I/flutter (30125): Gesture arena 14   ❙ Adding: HorizontalDragGestureRecognizer#69b8f(start behavior: start)
I/flutter (30125): Gesture arena 14   ❙ Closing with 3 members.
I/flutter (30125): Gesture arena 14   ❙ Rejecting: LongPressGestureRecognizer#9cad1(debugOwner: GestureDetector, state: possible)
I/flutter (30125): Gesture arena 14   ❙ Accepting: HorizontalDragGestureRecognizer#69b8f(start behavior: start)
I/flutter (30125): Gesture arena 14   ❙ Self-declared winner: HorizontalDragGestureRecognizer#69b8f(start behavior: start)

这是当您在拖动手势进行时设法将手势保持完全垂直(似乎在使用鼠标的模拟器上更容易)时发生的情况:

And this is what happens when you do manage to keep your gesture entirely vertical (seems to be easier on an emulator with a mouse), while the drag gesture is in process:

flutter: Gesture arena 30   ❙ ★ Opening new gesture arena.
flutter: Gesture arena 30   ❙ Adding: Instance of '_CombiningGestureArenaMember'
flutter: Gesture arena 30   ❙ Adding: HorizontalDragGestureRecognizer#11e7f(start behavior: down)
flutter: Gesture arena 30   ❙ Closing with 2 members.

即使轻微的垂直移动也会使HorizontalDragGestureRecognizer宣布获胜,但是VerticalDragGestureRecognizer(似乎包裹在_CombiningGestureArenaMember内部)从不要求胜利.实际上似乎完全被忽略了-在gestureRecognizers中带有VerticalDragGestureRecognizer的手势竞技场输出,如果没有,则完全相同.

Even a slight vertical move will make the HorizontalDragGestureRecognizer announce a win, but the VerticalDragGestureRecognizer (which seems to be wrapped inside the _CombiningGestureArenaMember) never claims a victory. It seems to be completely ignored in fact - the gesture arena output with VerticalDragGestureRecognizer in gestureRecognizers and without it is absolutely identical.

这可能是Flutter中的错误,所以我还创建了关于Flutter的GitHub的问题.但是,无论哪种方式-如何在当前版本的Flutter中达到这种效果?任何解决方法或规范解决方案将不胜感激.

It may be a bug in Flutter so I also created an issue on Flutter's GitHub. But either way - how do I achieve this effect with the current version of Flutter? Any workarounds or canonical solutions would be highly appreciated.

推荐答案

竞技场的规则似乎已经改变.现在,竞技场宣布具有主动接收器的手势赢得了胜利.确实,这甚至可以更大程度地提高手势的响应速度.但是,由于本机视图不声明手势,而仅在没有其他活动检测器/接收器声明手势时才使用手势,因此我怀疑垂直拖动甚至不会从WebView作为手势进入竞技场.这就是为什么任何轻微的水平拖动都会导致水平拖动手势获胜的原因-因为根本没有其他小部件可以要求任何手势.

It seems that the rules of the arena have changed. Now the arena declares wins for gestures that have active receivers. That indeed increases the responsiveness of the gestures even more. However, as the native views do not claim the gestures and only consume them when no other active detector/receiver claims them, I suspect that the vertical drag doesn't even enter the arena as a gesture from the WebView. That is why any slight horizontal drag causes horizontal drag gesture to win - because simply no other widgets claim any gesture.

您可以扩展VerticalDragGestureRecognizer,因此它可以接受手势:

You can extend VerticalDragGestureRecognizer, so it accepts gestures:

class PlatformViewVerticalGestureRecognizer
    extends VerticalDragGestureRecognizer {
  PlatformViewVerticalGestureRecognizer({PointerDeviceKind kind})
      : super(kind: kind);

  Offset _dragDistance = Offset.zero;

  @override
  void addPointer(PointerEvent event) {
    startTrackingPointer(event.pointer);
  }

  @override
  void handleEvent(PointerEvent event) {
    _dragDistance = _dragDistance + event.delta;
    if (event is PointerMoveEvent) {
      final double dy = _dragDistance.dy.abs();
      final double dx = _dragDistance.dx.abs();

      if (dy > dx && dy > kTouchSlop) {
        // vertical drag - accept
        resolve(GestureDisposition.accepted);
        _dragDistance = Offset.zero;
      } else if (dx > kTouchSlop && dx > dy) {
        // horizontal drag - stop tracking
        stopTrackingPointer(event.pointer);
        _dragDistance = Offset.zero;
      }
    }
  }

  @override
  String get debugDescription => 'horizontal drag (platform view)';

  @override
  void didStopTrackingLastPointer(int pointer) {}
}

之后,您可以在gestureRecognizers中使用新类:

After that, you can use the new class in gestureRecognizers:

PageView.builder(
  itemCount: 5,
  itemBuilder: (context, index) {
    return WebView(
      initialUrl: 'https://flutter.dev/docs',
      gestureRecognizers: [
        Factory(() => PlatformViewVerticalGestureRecognizer()),
      ].toSet(),
    );
  },
);

这篇关于将水平滚动与WebView结合使用时的滚动优先级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆