Xamarin.Forms WebView:e.Cancel = true 在使用自定义 WebViewRenderer 时,导航事件在 Android 上不起作用

Xamarin.Forms WebView: e.Cancel = true in Navigating event is not working on Android when using custom WebViewRenderer

我想在我的 WebView 中将打开的 URL 重定向到外部浏览器,所以我挂钩了它的 Navigated 事件:

webView.Navigating += (s, e) =>
{
    if (IsExternalUrl(e.Url))
    {
        try
        {
            var uri = new Uri(e.Url);
            Device.OpenUri(uri);        // show external links in external browser
        }
        catch (Exception)
        {
        }
            
        e.Cancel = true;   // <- not having any effects on Android
    }
};

在 Android 下,这导致 URL 在 Chrome 和 WebView 中同时打开。 iOS e.Cancel = true 确实按预期工作。

我在网上进行了大量搜索,但没有找到对我有帮助的东西,包括这个 Xamarin 论坛帖子:https://forums.xamarin.com/discussion/144314/using-webviewrenderer-and-webviewclient-causes-cancel-navigation-not-working

我现在有一个解决方法,使用后退导航:

XAML:

<local:HybridWebView x:Name="webView" CanGoBack="True"  WidthRequest="1000" HeightRequest="1000" />

后面的代码:

webView.Navigated += (s, e) =>
{
    if (e.Result == WebNavigationResult.Success)
    {
        if (Device.RuntimePlatform == Device.Android)  // on Android, prohibit webview from mirroring urls 
            if (IsExternalUrl(e.Url))                  // that are shown on external browser
                webView.GoBack();                      // this is necessary because e.Cancel = true doesn't 
    }                                                  // work in webView.Navigating() event
};

注意:最初,Navigated 事件没有触发,所以我用具有此覆盖功能的 WebViewClient 拉动了我的 HybridWebView

public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
    RaisePageFinishedEvent(url, view.Title);

    ...
    ...

    var source = new UrlWebViewSource { Url = url };
    var args = new WebNavigatedEventArgs(WebNavigationEvent.NewPage, source, url, WebNavigationResult.Success);
    _renderer.ElementController.SendNavigated(args);
}

我目前正在使用 Xamarin.Forms 4.5.

以编程方式按下后退按钮是一种非常粗糙的解决方法。因此,非常感谢 url 打开事件实际上被取消的解决方案。

更新:

我不得不删除 XAML 中的 CanGoBack="True" 并在我的 WebViewRenderer:

OnElementChanged 中硬编码 属性
(Element as IWebViewController).CanGoBack = true;

在 XAML 中设置 CanGoBack 属性 在附加 VS 2019 调试器 运行 时工作正常,但如果 运行 独立,应用程序立即关闭。可以在没有自定义渲染器的简单 WebView 上重现:

ApplyPropertiesVisitor.SetPropertyValue (System.Object xamlelement, Xamarin.Forms.Xaml.XmlName propertyName, System.Object value, System.Object rootElement, Xamarin.Forms.Xaml.INode node, Xamarin.Forms.Xaml.HydrationContext context, System.Xml.IXmlLineInfo lineInfo)
ApplyPropertiesVisitor.Visit (Xamarin.Forms.Xaml.ValueNode node, Xamarin.Forms.Xaml.INode parentNode)
ValueNode.Accept (Xamarin.Forms.Xaml.IXamlNodeVisitor visitor, Xamarin.Forms.Xaml.INode parentNode)
ElementNode.Accept (Xamarin.Forms.Xaml.IXamlNodeVisitor visitor, Xamarin.Forms.Xaml.INode parentNode)
ElementNode.Accept (Xamarin.Forms.Xaml.IXamlNodeVisitor visitor, Xamarin.Forms.Xaml.INode parentNode)
RootNode.Accept (Xamarin.Forms.Xaml.IXamlNodeVisitor visitor, Xamarin.Forms.Xaml.INode parentNode)
XamlLoader.Visit (Xamarin.Forms.Xaml.RootNode rootnode, Xamarin.Forms.Xaml.HydrationContext visitorContext, System.Boolean useDesignProperties)
XamlLoader.Load (System.Object view, System.String xaml, System.Reflection.Assembly rootAssembly, System.Boolean useDesignProperties)
XamlLoader.Load (System.Object view, System.String xaml, System.Boolean useDesignProperties)
XamlLoader.Load (System.Object view, System.Type callingType)
Extensions.LoadFromXaml[TXaml] (TXaml view, System.Type callingType)
WebPageCollabora.InitializeComponent ()
CloudplanMobileClient.WebPageCollabora..ctor (System.String url) [0x00031] in <6b79d357cd4641c5bd9a69278958d871>:0
WebPage+<>c__DisplayClass9_0.<OpenNewPage>b__0 ()
Thread+RunnableImplementor.Run ()
IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this)
(wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.27(intptr,intptr)

我想知道为什么这个错误明显 has been fixed in 2020 而我仍然遇到这个问题。然后我注意到该修复仅适用于 FormsWebViewClient 而不是适用于本机 Xamarin.Android class WebViewClient。我只是通过从 FormsWebViewClient 而不是 WebViewClient 派生我的 HybridWebViewRenderer 中使用的 Web 客户端并稍微修改了构造函数来解决了这个问题:

using Xamarin.Forms.Platform.Android;

...

namespace MyApp.Droid
{    
    // note: class was derived from 'WebViewClient' before
    public class JavascriptWebViewClient : FormsWebViewClient  
    {
        HybridWebViewRenderer _renderer;
        string _javascript;

        // note: now also calling base class constructor with renderer as parameter
        public JavascriptWebViewClient(string javascript, HybridWebViewRenderer renderer) : base(renderer)
        {
            _javascript = javascript;
            _renderer = renderer ?? throw new ArgumentNullException("renderer");
        }

    ...

    }
}