MAUI:如何禁用Android和iOS自定义WebView滚动实现整体滚动?
解决方案:禁用WebView内部滚动,让外层ScrollView控制整体滚动
要实现Image和WebView内容一起随外层ScrollView滚动,必须彻底禁用WebView的内部滚动机制,并让WebView高度自适应其HTML内容,这样外层ScrollView才能接管整个布局的滚动。以下是Android和iOS端的具体修改方案:
一、Android端修改(自定义渲染器)
替换原有触摸监听方式,直接操作WebView底层组件禁用滚动,同时通过JS传递内容高度实现WebView自适应:
[assembly: ExportRenderer(typeof(DailySaintAndroidWebView), typeof(DailySaintAndroidRenderer))] namespace OfficeName.Droid.Renderer { public class DailySaintAndroidRenderer : WebViewRenderer { public DailySaintAndroidRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Microsoft.Maui.Controls.WebView> e) { base.OnElementChanged(e); if (Control != null) { // 基础配置 Control.Settings.DefaultFontSize = 25; Control.Settings.JavaScriptEnabled = true; Control.Settings.DomStorageEnabled = true; // 彻底禁用WebView内部滚动 Control.ScrollEnabled = false; Control.VerticalScrollBarEnabled = false; Control.HorizontalScrollBarEnabled = false; // 设置WebView高度自适应内容 Control.SetWebViewClient(new CustomWebViewClient(InjectCssAndHeightScript())); // 注册JS交互接口 Control.AddJavascriptInterface(this, "android"); } } private string InjectCssAndHeightScript() { return @" let style = document.createElement('style'); style.innerHTML = 'body, p, h1, h2, h3, h4, h5, h6 { color: white !important; background-color: #0091DA !important; margin: 0; padding: 5px; }'; document.head.appendChild(style); // 通知原生端内容高度 window.android.notifyContentHeight(document.body.scrollHeight); "; } private class CustomWebViewClient : WebViewClient { private readonly string _injectScript; public CustomWebViewClient(string script) { _injectScript = script; } public override void OnPageFinished(Android.Webkit.WebView view, string url) { base.OnPageFinished(view, url); view.EvaluateJavascript(_injectScript, null); } } // 接收JS传递的内容高度,转换为设备像素后设置WebView高度 [JavascriptInterface] public void NotifyContentHeight(int height) { Device.BeginInvokeOnMainThread(() => { if (Element != null) { Element.HeightRequest = height * Resources.DisplayMetrics.Density; } }); } } }
二、iOS端修改(自定义渲染器)
禁用WKWebView的ScrollView滚动能力,通过JS消息传递内容高度实现自适应:
[assembly: ExportRenderer(typeof(DailySaintWebView), typeof(DailySaintWebViewRenderer))] namespace OfficeName.Platforms.iOS.Renderer { public class DailySaintWebViewRenderer : ViewRenderer<DailySaintWebView, WKWebView> { WKWebView _wkWebView; private WKUserContentController _userContentController; protected override void OnElementChanged(ElementChangedEventArgs<DailySaintWebView> e) { base.OnElementChanged(e); if (Control == null) { _userContentController = new WKUserContentController(); // 添加消息处理器接收JS传递的高度 _userContentController.AddScriptMessageHandler(new ContentHeightHandler(this), "notifyContentHeight"); var config = new WKWebViewConfiguration { AllowsInlineMediaPlayback = true, UserContentController = _userContentController }; _wkWebView = new WKWebView(CGRect.Empty, config); _wkWebView.BackgroundColor = UIColor.Clear; _wkWebView.ScrollView.BackgroundColor = UIColor.Clear; _wkWebView.Opaque = false; // 禁用内部滚动 _wkWebView.ScrollView.ScrollEnabled = false; _wkWebView.ScrollView.Bounces = false; _wkWebView.NavigationDelegate = new CustomNavigationDelegate(); SetNativeControl(_wkWebView); } } public class CustomNavigationDelegate : WKNavigationDelegate { public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation) { string fontSize = Device.Idiom == TargetIdiom.Phone ? "500%" : "750%"; string adjustAndNotifyScript = $"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '{fontSize}';" + "document.body.style.margin = '0'; document.body.style.padding = '5px';" + "window.webkit.messageHandlers.notifyContentHeight.postMessage(document.body.scrollHeight);"; string colorScript = @" let style = document.createElement('style'); style.innerHTML = 'body, p, h1, h2, h3, h4, h5, h6 { color: white !important; }'; document.head.appendChild(style); "; webView.EvaluateJavaScript(adjustAndNotifyScript, null); webView.EvaluateJavaScript(colorScript, null); } } // 处理JS传递的高度消息 private class ContentHeightHandler : NSObject, IWKScriptMessageHandler { private readonly DailySaintWebViewRenderer _renderer; public ContentHeightHandler(DailySaintWebViewRenderer renderer) { _renderer = renderer; } public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message) { if (message.Body is NSNumber heightNum) { var height = nfloat.Parse(heightNum.ToString()); Device.BeginInvokeOnMainThread(() => { _renderer.Element.HeightRequest = height; }); } } } protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if (e.PropertyName == "Url") { string finalHtml = Element.Url.Replace("width=\"640\"", "width=\"1000\""); Control.LoadHtmlString(finalHtml, null); } } protected override void Dispose(bool disposing) { if (disposing) { _userContentController?.RemoveScriptMessageHandler("notifyContentHeight"); } base.Dispose(disposing); } } }
三、布局调整
修改WebView的VerticalOptions为Start,让它根据内容高度自适应,避免强制填充剩余空间:
<Frame Padding="5" CornerRadius="20" Margin="0,10,0,0" Grid.Row="1" x:Name="android_webview_frame" BackgroundColor="#0091DA" IsVisible="False"> <ScrollView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"> <StackLayout BackgroundColor="#0091DA"> <Image x:Name="android_saint_image" Aspect="AspectFit" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" IsVisible="False" Margin="5"/> <local:DailySaintAndroidWebView x:Name="android_webview" BackgroundColor="#0091DA" HorizontalOptions="FillAndExpand" VerticalOptions="Start"/> <!-- 此处修改为Start --> </StackLayout> </ScrollView> </Frame>
关键说明
- 禁用滚动核心:Android直接设置
Control.ScrollEnabled = false,iOS禁用ScrollView.ScrollEnabled = false,比触摸拦截更彻底,避免滚动冲突。 - 高度自适应:通过JS获取HTML实际内容高度,传递给原生端设置WebView的
HeightRequest,确保WebView刚好包裹内容,无多余滚动空间。 - 布局配合:WebView使用
VerticalOptions="Start",避免强制拉伸,让外层ScrollView能正确计算整体滚动范围。
内容的提问来源于stack exchange,提问作者Matthew Pans
相关产品推荐
相关产品推荐

