Android WebView 呈现空白/白色,视图不会在 css 更改或 HTML 更改时更新,动画断断续续



每当我进行 css 类更改时,更改并不总是会出现。当我有一个触摸事件向按钮添加类似向下类名称的内容时,通常会发生这种情况。该按钮不会更新,页面上的任何其他内容也不会更新。它何时工作非常不稳定。我还注意到,有时我的元素显示为白色,没有任何内容或任何东西。这是非常令人沮丧的!

注意:
从Android 4.4+开始,有一个更好的解决方案。这是一个名为CrossWalk的直接WebView替代品。它使用最新的Chromium套件,非常棒。你可以在这里阅读它: crosswalk-project.org

此外,似乎从Android 4.4开始,不再需要invalidate()解决方案,您可以使用其他一些更安全的答案。我只会使用这种invalidate()方法作为最后的努力。


正在回答我自己的问题,希望能帮助人们解决与我相同的问题。

我已经尝试了几种方法让事情变得更好,即使是臭名昭著的 -webkit-transform: translate3d(0,0,0);即使这样也不太有效。

让我与您分享有效的方法。

首先,使用最新的 API。我正在使用 API 15。在AndroidManifest.xml中,请确保启用硬件加速。如果您的 API 版本不支持此功能,请转到下一位。

如果您的版本确实支持它,您可以通过修改清单来启用它:

<application
...
android:hardwareAccelerated="true">

此外,请确保您的清单具有您正在使用的清单支持的最低 API。由于我使用的是 API 15,因此我的清单具有以下内容:

<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="15" />

(更新:您现在应该在build.gradle中修改该值)

现在,在将在 Web 视图中显示的内容的主 CSS 文件中,添加如下内容:

body div {
-webkit-transform: translate3d(0,0,0);
}

在正文div位上添加任何其他元素类型;你可能可以排除图像,ulli等。将这种 CSS 样式应用于所有内容的原因只是通过反复试验,我发现将其暴力应用于所有内容似乎效果最好。对于更大的 DOM 树,您可能需要更具体。但是,我不确定规格是什么。

实例化WebView时,您需要设置一些设置:

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.loadUrl("file:///android_asset/www/index.html");
appView.getSettings().setRenderPriority(RenderPriority.HIGH);
appView.getSettings()
.setPluginState(WebSettings.PluginState.ON_DEMAND);
}

倒数第二,但至关重要的一点:我正在阅读WebView类的源代码,发现了这个关于强制重绘的小评论。有一个static final boolean,当设置为true时,将强制视图始终重绘。我对 Java 语法不是很大,但我认为你不能直接更改类的静态最终属性。所以我最终做的是像这样扩展类:

import org.apache.cordova.CordovaWebView;
import android.content.Context;
import android.graphics.Canvas;
public class MyWebView extends CordovaWebView {
public static final String TAG = "MyWebView";
public MyWebView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Warning! This will cause the WebView to continuously be redrawn
// and will drain the devices battery while the view is displayed!
invalidate();
}
}

请记住,我使用的是Cordova/PhoneGap,所以我不得不从CordovaWebView扩展。如果在 onDraw 方法中看到,则有一个对invalidate的调用。这将导致视图不断重绘。但是,我强烈建议添加逻辑以仅在需要时重绘。

如果您使用的是科尔多瓦,还有最后一步。您必须告诉PhoneGap使用您的新WebView类而不是他们自己的WebView类。在您的MainActivity类中,添加以下内容:

public void init(){
CordovaWebView webView = new MyWebView(MainActivity.this);
super.init(webView, new CordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView));
}

就是这样!尝试运行您的应用程序,看看是否一切顺利。在进行所有这些更改之前,页面将显示为白色,直到再次点击屏幕后才会应用CSS更改,动画非常断断续续,甚至不明显。我应该提到,动画仍然

断断续续,但比以前断断续续远。如果有人对此有任何补充,请在下面发表评论。我总是对优化持开放态度;我完全意识到可能有更好的方法来做我刚才建议的事情。

如果我上面的解决方案对您不起作用,您能否描述一下您的具体情况以及您在 AndroidWebView中看到的结果?

最后,我将此答案启用为"社区维基">,因此任何人和每个人都可以随时进行调整。

谢谢!


编辑:

使用最新的PhoneGap,您需要使init()方法看起来更像这样:

public void init(){
CordovaWebView webView = new MyWebView(MainActivity.this);
super.init(webView, new IceCreamCordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView));
}

我实现了凯尔的解决方案,它解决了这个问题。豪威尔 我注意到当应用程序打开时,android 4.0.4 上的电池消耗很大。同样在更改后,我有用户抱怨 swiftKey 键盘不再适用于我的应用程序。

我的应用程序中的每个更改都是由用户操作触发的,因此我想出了一个修改后的版本,该版本仅在 touchEvent 之后触发 invalidate():

Handler handler = new Handler();
public boolean onTouchEvent (MotionEvent event){
super.onTouchEvent(event);
handler.postDelayed(triggerInvalidate, 60);
handler.postDelayed(triggerInvalidate, 300);
return true;
}
private Runnable triggerInvalidate=new Runnable(){
public void run(){
invalidate();
}
};

从来没有用Java做过任何编程,所以可能有更好的解决方案来做到这一点。

re:重绘问题,您可以通过从元素中读取属性来强制重绘

所以说你这样做:

$('#myElement').addClass('foo'); // youre not seeing this take effect

如果之后执行此操作:

$('#myElement').width();

它将强制重绘。

这样,您可以有选择性地而不是一直重绘整个页面,这很昂贵

对我来说,这个问题只发生在三星设备上。我能够通过禁用 Web 视图的硬件加速来修复它:

webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

希望对您有所帮助。

如上所述和其他地方 - 覆盖View.onDraw()并调用View.invalidate将导致电池不满意/应用程序性能会下降。您也可以像这样手动invalidate调用x ms

/**
* Due to bug in 4.2.2 sometimes the webView will not draw its contents after the data has loaded. 
* Triggers redraw. Does not work in webView's onPageFinished callback for some reason
*/
private void forceWebViewRedraw()
{
mWebView.post(new Runnable() {
@Override
public void run()
{
mWebView.invalidate();
if(!isFinishing())
mWebView.postDelayed(this, 1000);
}
});
}

我尝试在WebViewClient.onPageLoaded()中放置无效调用,但这似乎不起作用。虽然我的解决方案可以更好,但它很简单,它对我有用(我只是显示一个推特登录)

另请参阅我在 WebView 上的回答,直到触摸 Android 4.2.2 才能渲染,这个想法是将@Olivier的功能导出到 JavaScript 中,以便您可以在合理的部分触发 JS 的失效......我的上帝,又是一个黑客!:)

我遇到了这个问题,因为在PageStared和onPageDone上我显示并隐藏了加载动画。

由于注入JS做我的菜单和原始网站的东西,并注意到在onPageFinish上注入JS强制Web视图绘制内容。下面是您可以在 onPageDone 末尾添加的内容的示例

view.loadUrl("javascript:(function() { var select = document.getElementsByClassName('something')[0]rn" + 
"                     if(select)" +
"                       select.style.display = 'none';})()");

只需在oncreate()中清除webView的缓存,它对我有用。

webView.clearCache(true);

您应该通过修改清单来启用硬件:

<application
...
android:hardwareAccelerated="true">

并在您的活动中恢复()

webView.postDelayed(() -> {
if (!isFinishing() && webView != null) {
webView.setLayerType(View.LAYER_TYPE_NONE, null);
}
}, 1000);

在你的暂停()

webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

最新更新