You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

关键说明

  1. 禁用滚动核心:Android直接设置Control.ScrollEnabled = false,iOS禁用ScrollView.ScrollEnabled = false,比触摸拦截更彻底,避免滚动冲突。
  2. 高度自适应:通过JS获取HTML实际内容高度,传递给原生端设置WebView的HeightRequest,确保WebView刚好包裹内容,无多余滚动空间。
  3. 布局配合:WebView使用VerticalOptions="Start",避免强制拉伸,让外层ScrollView能正确计算整体滚动范围。

内容的提问来源于stack exchange,提问作者Matthew Pans

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 13:33:08