首页 > 解决方案 > 如何根据动态 HTML 在 Xamarin.Android(或 Android)中自动调整 WebView 的高度?

问题描述

我正在创建一个从 API 获取动态 HTML 网站的应用程序。我的主要挑战是我需要实时自动调整,因为height该属性没有按预期工作并且不会自动调整,它始终是,并且从未显示。WebViewandroid:layout_height="wrap_content"height0dp

我使用的 API 可以返回一个简单的文本,如下所示:

<p>Lorem ipsum dolor sit amet consectetur adipiscing elit, malesuada per consequat conubia accumsan vulputate tincidunt, porttitor cum cras pretium diam inceptos. Augue fringilla id laoreet metus quisque eu accumsan ultrices fusce, vel hendrerit phasellus mollis arcu consequat risus suscipit, nostra orci lobortis at quis sed integer cubilia. Quisque turpis congue euismod class tristique magna at eros aenean quam, cum facilisis malesuada per auctor cubilia leo ultrices pharetra, praesent habitant ut nec feugiat velit pulvinar libero tellus.</p>

或带有图像、表格、次要格式等的复杂 HTML。

就我而言,我尝试将WebView高度设置为wrap_content(如前所述)并作为一种解决方法,同时,我在加载前将其隐藏,完成后重新加载,并在内容完全后再次显示加载,但没有任何工作正常。heightwas 或者是一个小0dp数字32dp,几乎没有意义和不可见,因为几乎每个 HTML 都很长。

这是我当前的代码,其中包含一些潜在的示例进行测试:

XML:

<?xml version="1.0" encoding="UTF-8" ?>
<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">
    <LinearLayout
        android:id="@+id/llOverview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="@dimen/min_val"
        android:orientation="vertical">
        <ProgressBar
            android:id="@+id/indeterminateBar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:indeterminate="true"
            android:max="100"
            android:layout_height="wrap_content" />
        <TextView
            android:layout_marginTop="@dimen/min_half_val"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="@dimen/text_size"
            android:id="@+id/lblDescription" />
        <android.webkit.WebView
            android:id="@+id/webViewExtra"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</ScrollView>

C#:

var lblDescription = FindViewById<TextView>(Resource.Id.lblDescription);

lblDescription.Text = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Potenti nullam ac tortor vitae purus faucibus ornare. Tellus elementum sagittis vitae et leo duis. Condimentum mattis pellentesque id nibh tortor id aliquet lectus proin. Elementum curabitur vitae nunc sed velit dignissim sodales ut. Vitae aliquet nec ullamcorper sit amet risus nullam eget. Quis imperdiet massa tincidunt nunc pulvinar sapien et ligula. Quam adipiscing vitae proin sagittis. Bibendum at varius vel pharetra vel turpis nunc. Bibendum at varius vel pharetra vel. Id diam vel quam elementum. Magna etiam tempor orci eu lobortis elementum nibh tellus. Ligula ullamcorper malesuada proin libero nunc. Gravida quis blandit turpis cursus. Ut pharetra sit amet aliquam id diam maecenas. Nisl rhoncus mattis rhoncus urna neque. Tempus egestas sed sed risus pretium quam vulputate dignissim. Et netus et malesuada fames ac. Malesuada fames ac turpis egestas maecenas pharetra convallis.";

var webView = FindViewById<Android.Webkit.WebView>(Resource.Id.webViewExtra);

webView.Visibility = ViewStates.Gone;
webView.Settings.VerticalScrollBarEnabled = true;
webView.Settings.JavaScriptEnabled = true;

var html = "<p>Lorem ipsum dolor sit amet consectetur adipiscing elit, malesuada per consequat conubia accumsan vulputate tincidunt, porttitor cum cras pretium diam inceptos. Augue fringilla id laoreet metus quisque eu accumsan ultrices fusce, vel hendrerit phasellus mollis arcu consequat risus suscipit, nostra orci lobortis at quis sed integer cubilia. Quisque turpis congue euismod class tristique magna at eros aenean quam, cum facilisis malesuada per auctor cubilia leo ultrices pharetra, praesent habitant ut nec feugiat velit pulvinar libero tellus.</p><p>Lorem ipsum dolor sit amet consectetur adipiscing elit, malesuada per consequat conubia accumsan vulputate tincidunt, porttitor cum cras pretium diam inceptos. Augue fringilla id laoreet metus quisque eu accumsan ultrices fusce, vel hendrerit phasellus mollis arcu consequat risus suscipit, nostra orci lobortis at quis sed integer cubilia. Quisque turpis congue euismod class tristique magna at eros aenean quam, cum facilisis malesuada per auctor cubilia leo ultrices pharetra, praesent habitant ut nec feugiat velit pulvinar libero tellus.</p><p>Lorem ipsum dolor sit amet consectetur adipiscing elit, malesuada per consequat conubia accumsan vulputate tincidunt, porttitor cum cras pretium diam inceptos. Augue fringilla id laoreet metus quisque eu accumsan ultrices fusce, vel hendrerit phasellus mollis arcu consequat risus suscipit, nostra orci lobortis at quis sed integer cubilia. Quisque turpis congue euismod class tristique magna at eros aenean quam, cum facilisis malesuada per auctor cubilia leo ultrices pharetra, praesent habitant ut nec feugiat velit pulvinar libero tellus.</p><p>Lorem ipsum dolor sit amet consectetur adipiscing elit, malesuada per consequat conubia accumsan vulputate tincidunt, porttitor cum cras pretium diam inceptos. Augue fringilla id laoreet metus quisque eu accumsan ultrices fusce, vel hendrerit phasellus mollis arcu consequat risus suscipit, nostra orci lobortis at quis sed integer cubilia. Quisque turpis congue euismod class tristique magna at eros aenean quam, cum facilisis malesuada per auctor cubilia leo ultrices pharetra, praesent habitant ut nec feugiat velit pulvinar libero tellus.</p><p>Lorem ipsum dolor sit amet consectetur adipiscing elit, malesuada per consequat conubia accumsan vulputate tincidunt, porttitor cum cras pretium diam inceptos. Augue fringilla id laoreet metus quisque eu accumsan ultrices fusce, vel hendrerit phasellus mollis arcu consequat risus suscipit, nostra orci lobortis at quis sed integer cubilia. Quisque turpis congue euismod class tristique magna at eros aenean quam, cum facilisis malesuada per auctor cubilia leo ultrices pharetra, praesent habitant ut nec feugiat velit pulvinar libero tellus.</p><p>Lorem ipsum dolor sit amet consectetur adipiscing elit, malesuada per consequat conubia accumsan vulputate tincidunt, porttitor cum cras pretium diam inceptos. Augue fringilla id laoreet metus quisque eu accumsan ultrices fusce, vel hendrerit phasellus mollis arcu consequat risus suscipit, nostra orci lobortis at quis sed integer cubilia. Quisque turpis congue euismod class tristique magna at eros aenean quam, cum facilisis malesuada per auctor cubilia leo ultrices pharetra, praesent habitant ut nec feugiat velit pulvinar libero tellus.</p><p>Lorem ipsum dolor sit amet consectetur adipiscing elit, malesuada per consequat conubia accumsan vulputate tincidunt, porttitor cum cras pretium diam inceptos. Augue fringilla id laoreet metus quisque eu accumsan ultrices fusce, vel hendrerit phasellus mollis arcu consequat risus suscipit, nostra orci lobortis at quis sed integer cubilia. Quisque turpis congue euismod class tristique magna at eros aenean quam, cum facilisis malesuada per auctor cubilia leo ultrices pharetra, praesent habitant ut nec feugiat velit pulvinar libero tellus.</p><p>Lorem ipsum dolor sit amet consectetur adipiscing elit, malesuada per consequat conubia accumsan vulputate tincidunt, porttitor cum cras pretium diam inceptos. Augue fringilla id laoreet metus quisque eu accumsan ultrices fusce, vel hendrerit phasellus mollis arcu consequat risus suscipit, nostra orci lobortis at quis sed integer cubilia. Quisque turpis congue euismod class tristique magna at eros aenean quam, cum facilisis malesuada per auctor cubilia leo ultrices pharetra, praesent habitant ut nec feugiat velit pulvinar libero tellus.</p>";

if (!string.IsNullOrEmpty(html))
{

    html = $@"<!DOCTYPE html>
            <html>
                <head>
                    <meta charset='utf-8' />
                    <meta name='viewport' content='width=device-width, initial-scale=1'>
                </head>
                <body style='margin: 0px; background: #fafafa'>{html}</body>
            </html>";

    webView.SetWebViewClient(new JSOverviewHelper());

    webView.LoadData(html, "text/html", "UTF-8");

    webView.Reload();
}

public class JSOverviewHelper : WebViewClient
{
    public override void OnPageFinished(WebView webView, string url)
    {
        base.OnPageFinished(webView, url);

        webView.Visibility = Android.Views.ViewStates.Visible;
    }
}

除此之外,我已经尝试height使用 JS 函数来计算并没有按预期工作。返回的height往往是当前heightWebView + 1px。但是,如果我使用chrome://inspectthen 运行相同的函数,它会返回正确的奇数值。

OnPageFinished这是使用以下事件测试此实验的函数WebView

public override void OnPageFinished(WebView webView, string url)
{
    base.OnPageFinished(webView, url);

    webView.Visibility = ViewStates.Visible;

    Thread.Sleep(1000);

    webView.EvaluateJavascript(@"(function() {
                                    var body = document.body,
                                        html = document.documentElement;

                                    return Math.max(body.scrollHeight, body.offsetHeight, 
                                                            html.clientHeight, html.scrollHeight, html.offsetHeight);
                                        })();", new WebViewValueCallback(webView));
}

public class WebViewValueCallback : Object, IValueCallback
{
    private readonly WebView webView;

    public WebViewValueCallback(WebView webView)
    {
        this.webView = webView;
    }

    public void OnReceiveValue(Object value)
    {
        var result = Convert.ToString(value);
        ViewGroup.LayoutParams vc = webView.LayoutParameters;
        vc.Height = int.Parse(Convert.ToString(value));
        webView.LayoutParameters = vc;
    }
}

此外,我尝试使用OnPageCommitVisible(之前触发过OnPageFinished)并OnProgressChanged使用基于此处WebChromeClient建议的自定义类,当它达到 100% 时,JS 函数仍然返回与预期不同的值,它总是返回 0 或当前和以前一样的高度。WebView + 1px

我还尝试了什么?我在测试应用程序中设置了预定义的高度WebView,创建了一个静态页面,并使用 JS 加载了数据,但返回的高度仍然不正确。

知道如何在加载后将高度调整为最新高度吗?谢谢。

PS:

标签: androidxamarin.androidandroid-webviewheight

解决方案


经过几次尝试,我能够为我的案例创建一个“可行”的解决方案:

  1. 使用以下配置修改您的WebView并将其包围:LinearLayout
<LinearLayout
    android:id="@+id/llWebViewExtras"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:orientation="vertical">
    <android.webkit.WebView
        android:id="@+id/webViewExtra"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
  1. 在您的 C# 部分中添加此配置并使用您的LinearLayout.
public static LinearLayout LlWebViewExtras { get; set; }
private WebView webView;

protected override void OnCreate(Bundle savedInstanceState)
{
    LlWebViewExtras = FindViewById<LinearLayout>(Resource.Id.llWebViewExtras);
    webView = FindViewById<WebView>(Resource.Id.webViewExtra);

    webView.Settings.JavaScriptEnabled = true;
    webView.VerticalScrollBarEnabled = true;
    string script = $"javascript:myFunction();";
    webView.AddJavascriptInterface(new CallJSInterface(), "CSharp");
    webView.SetWebViewClient(new JSHelper(script));
    webView.LoadUrl("file:///android_asset/website/mywebsite.html");
}

public class JSHelper : WebViewClient
{
    private readonly string script;

    public JSHelper(string script)
    {
        this.script = script;
    }

    public override void OnPageFinished(WebView webView, string url)
    {
        base.OnPageFinished(webView, url);

        webView.Visibility = ViewStates.Visible;

        webView.EvaluateJavascript(script, null);
    }
}

  1. 创建一个捕获高度的函数,修改LinearLayout高度,并在主线程中将其更改为dps。
public class CallJSInterface : Java.Lang.Object
{
    [Export]
    [JavascriptInterface]
    public void CurrentHeight(int height)
    {
        MainThread.BeginInvokeOnMainThread(() =>
        {
            var llWebViewExtras = MyActivity.LlWebViewExtras;

            using ViewGroup.LayoutParams vc = llWebViewExtras.LayoutParameters;
            vc.Height = ConvertPixelsToDp(height);
            llWebViewExtras.LayoutParameters = vc;
        });
    }

    public static int ConvertPixelsToDp(float px)
    {
        return (int) (px * Resources.System.DisplayMetrics.Density);
    }
}
  1. 在您的 JS 中创建一个以 px 为单位返回最大高度的函数:
function myFunction() {
    CSharp.CurrentHeight(latestHeight());
}

function getLatestHeight() {
    let cHeight = getHeight();

    if (cHeight === 0) {
        //content is the element that contains the data. I used a Div
        let content = document.getElementById("content");
        let contentTmp = document.getElementById("content");
        contentTmp = contentTmp.cloneNode(true);
        contentTmp.id = "tmpcontent";
        content.style.visibility = "hidden";
        contentTmp.offsetHeight + 0;
        document.body.appendChild(contentTmp);
        document.body.removeChild(document.getElementById("content"));
        content.style.visibility = "visible";

        return getHeight();
    }
    
    return cHeight;
}

function getHeight() {
    let body = document.body,
        html = document.documentElement;

    return Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight, body.getBoundingClientRect().height);
}

我提出的这个解决方案很好,但它不是防弹的。它往往会随着时间的推移而失败并返回 0 作为它的height. 我什至配置为WebView在这些情况下重新加载并每隔几毫秒检查几次它的高度,但没有任何成功的结果。

我根据这个答案添加了一些额外的验证:https ://stackoverflow.com/a/27729544/1928691

它使高度加倍,但至少它的高度不会返回 0。

此外,我为这些情况创建了这个额外的解决方法:

webView.SetOnTouchListener(new OnTouchListener(webView));

public class OnTouchListener : Java.Lang.Object, View.IOnTouchListener
{
    private readonly int maxTop;
    private readonly int maxBottom;
    private readonly WebView webView;

    private float downX, downY;
    private int totalY;
    private int scrollByX, scrollByY;

    public OnTouchListener(WebView webView)
    {
        var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
        // set scroll limits
        maxTop = 0;
        maxBottom = (int)mainDisplayInfo.Height;

        this.webView = webView;
    }

    public bool OnTouch(View v, MotionEvent e)
    {
        float currentX, currentY;

        if (e.Action == MotionEventActions.Down)
        {
            downX = e.GetX();
            downY = e.GetY();
        }
        else if (e.Action == MotionEventActions.Move)
        {
            currentX = e.GetX();
            currentY = e.GetY();
            scrollByX = (int)(downX - currentX);
            scrollByY = (int)(downY - currentY);

            // scrolling to top of image (pic moving to the bottom)
            if (currentY > downY)
            {
                if (totalY == maxTop)
                {
                    scrollByY = 0;
                }
                if (totalY > maxTop)
                {
                    totalY += scrollByY;
                }
                if (totalY < maxTop)
                {
                    scrollByY = maxTop - (totalY - scrollByY);
                    totalY = maxTop;
                }
            }

            // scrolling to bottom of image (pic moving to the top)
            if (currentY < downY)
            {
                if (totalY == maxBottom)
                {
                    scrollByY = 0;
                }
                if (totalY < maxBottom)
                {
                    totalY += scrollByY;
                }
                if (totalY > maxBottom)
                {
                    scrollByY = maxBottom - (totalY - scrollByY);
                    totalY = maxBottom;
                }
            }

            webView.ScrollBy(scrollByX, scrollByY);
            downX = currentX;
            downY = currentY;
        }

        return true;
    }
}

但是,如果您知道如何修复它并达到真正的高度,我会非常高兴。

PS:

我什至在 Chromium 项目中开了一张票,他们接受了最后一部分作为错误:

https://bugs.chromium.org/p/chromium/issues/detail?id=1232745


推荐阅读