android - 如何根据动态 HTML 在 Xamarin.Android(或 Android)中自动调整 WebView 的高度?
问题描述
我正在创建一个从 API 获取动态 HTML 网站的应用程序。我的主要挑战是我需要实时自动调整,因为height
该属性没有按预期工作并且不会自动调整,它始终是,并且从未显示。WebView
android:layout_height="wrap_content"
height
0dp
我使用的 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
(如前所述)并作为一种解决方法,同时,我在加载前将其隐藏,完成后重新加载,并在内容完全后再次显示加载,但没有任何工作正常。height
was 或者是一个小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
往往是当前height
的WebView + 1px
。但是,如果我使用chrome://inspect
then 运行相同的函数,它会返回正确的奇数值。
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:
- 如果您知道如何在 Java 或 Kotlin 中修复它,我很可能会知道如何将其转换为 C# 代码。从我的角度来看,这仍然是一个有效的答案。
- 这
TextView
是强制性的,因为它在网站之前显示了一个可选的摘录。因此,它不能被删除。
解决方案
经过几次尝试,我能够为我的案例创建一个“可行”的解决方案:
- 使用以下配置修改您的
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>
- 在您的 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);
}
}
- 创建一个捕获高度的函数,修改
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);
}
}
- 在您的 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
推荐阅读
- sql - 在 Microsoft SQL Server 上创建\删除表需要什么权限?dbowner 好吗?
- python - Sqlalchemy 和 Python - 返回 ID 的 upsert 语句
- security - 从 Rails 5.1.6.2 升级到 6.0.0 时,如何使加密的凭据正常工作?
- r - 比较两个数据集以查找 r 中的一个数据集中不存在的行
- json - 如何在空手道中使用变量作为 json 键?
- neural-network - ConvNet+FCN示例中的FCN维度
- asp.net-core - 登录后重定向到服务器
- php - 根据“WHERE”关闭条件加载更多按钮值
- c# - 我有一个 C# 开关,我需要在每个案例返回之前调用一个方法。有什么办法可以简化这个吗?
- excel - VBA读取下拉列表中的第一个选择并运行宏