使 WebView 的列表可滚动

Make WebView's list scrollable

我有一个看起来像这样的页面*:

<ScrollView>
  <WebView/>
  <WebView/>
  <WebView/>
</Scrollview>

* 不是实际布局,但它足以解决问题并使问题更清楚。

我不知道 WebView 到底是什么样子,因为它可能变化很大。 WebView可以包含可滚动列表。

我想实现这种滚动行为:

这是两个 WebView(蓝色 header 和绿色 header 的一个)。我只想在列表中拖动时以蓝色 WebView 滚动列表。如果我触摸其他任何地方(例如蓝色或绿色 header),我想在 ScrollView.

中滚动

我尝试解决这个问题是 WebViewCustomRenderer,我在 this thread in the Xamarin forums:

中找到
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
    base.OnElementChanged(e);

    if (e.OldElement != null)
    {
        Control.Touch -= Control_Touch;
    }

    if (e.NewElement != null)
    {
        Control.Touch += Control_Touch;
    }
}

void Control_Touch(object sender, TouchEventArgs e)
{
    // Executing this will prevent the Scrolling to be intercepted by parent views
    switch (e.Event.Action)
    {
        case MotionEventActions.Down:
            Control.Parent.RequestDisallowInterceptTouchEvent(true);
            break;
        case MotionEventActions.Up:
            Control.Parent.RequestDisallowInterceptTouchEvent(false);
            break;
    }
    // Calling this will allow the scrolling event to be executed in the WebView
    Control.OnTouchEvent(e.Event);
}

这几乎完成了工作,我现在可以在 WebView 内的列表中滚动,但如果我点击 [=] 中的其他任何地方,它将不再在 ScrollView 中滚动13=].

所以我尝试检查内部列表的高度:

case MotionEventActions.Down:
    if (ComputeVerticalScrollRange() > MeasuredHeight)
        Control.Parent.RequestDisallowInterceptTouchEvent(true);
    break;
case MotionEventActions.Up:
    if (ComputeVerticalScrollRange() > MeasuredHeight)
        Control.Parent.RequestDisallowInterceptTouchEvent(false);
    break;

MeasuredHeightWebView的高度。 ComputeVerticalScrollRange() 将始终 return 与 MeasuredHeight 相同的高度。因此,一种可能的解决方案是获取 WebView 中内部列表的高度,但我不知道如何获取(我是 Android 的新手)。也许,甚至还有其他解决方案。

对于代码 Control.Parent.RequestDisallowInterceptTouchEvent(true);,您需要确保此 Control.Parent 确实是 ScrollViewer

AFAIK,为每个平台实现视图,它使用渲染器,Control.Parent 这里是 WebViewRenderer,所以我认为你需要的可能是这个:

Control.Parent.Parent.Parent.Parent.RequestDisallowInterceptTouchEvent(true);

真正的问题是何时将其设置为 true 或 false,例如,您可以像这样修改 Control_Touch 事件:

private void Control_Touch(object sender, TouchEventArgs e)
{
    var viewheight = Control.MeasuredHeight;
    var height = (int)Math.Floor(Control.ContentHeight * Control.Scale);
    switch (e.Event.Action)
    {
        case MotionEventActions.Down:
            downY = e.Event.GetY();
            mAction = ActionState.None;
            Control.Parent.Parent.Parent.Parent.RequestDisallowInterceptTouchEvent(true);
            break;

        case MotionEventActions.Move:
            upY = e.Event.GetY();
            var delta = downY - upY;
            if (Math.Abs(delta) > MIN_DISTANCE)
            {
                if (delta < 0)
                {
                    mAction = ActionState.TB;
                    //top reached
                    if (Control.ScrollY == 0)
                    {
                        Control.Parent.Parent.Parent.Parent.RequestDisallowInterceptTouchEvent(false);
                    }
                }
                else if (delta > 0)
                {
                    mAction = ActionState.BT;

                    //bottom reached
                    if (Control.ScrollY + viewheight + 5 >= height)
                    {
                        Control.Parent.Parent.Parent.Parent.RequestDisallowInterceptTouchEvent(false);
                    }
                }
            }
            break;

        case MotionEventActions.Up:
            Control.Parent.Parent.Parent.Parent.RequestDisallowInterceptTouchEvent(true);
            break;
    }
    Control.OnTouchEvent(e.Event);
}

我使用了两个自定义 WebView 来测试我的演示,想法是当 WebView 到达顶部并且从上到下滚动时禁用滚动,反之亦然。你可以试试。