每当我进行 css 类更改时,更改并不总是出现。当我有一个向按钮添加类似向下类名称的触摸事件时,这似乎会发生。该按钮不会更新,页面上的其他任何内容也不会更新。它的工作时间非常不稳定。我还注意到,有时我的元素看起来是白色的,没有任何内容或任何东西。这非常令人沮丧!
9 回答
注意:
Android 4.4+ 有更好的解决方案。这是一个WebView
名为CrossWalk的替代品。它使用最新的 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
)
现在,在您的主要 CSS 文件中,将在 WebView 中呈现的内容,添加如下内容:
body div {
-webkit-transform: translate3d(0,0,0);
}
使用您拥有的任何其他元素类型添加到 body div 位;您可能可以排除图像、、、ul
等li
。将这种 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
. 这将导致视图不断重绘。但是,我强烈建议添加逻辑以仅在需要时重绘。
如果您使用的是 Cordova,还有最后一步。您必须告诉 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 更改,动画非常不稳定甚至不明显。我应该提到动画仍然不稳定,但远不如以前那么不稳定。
如果有人对此有任何补充,请在下方发表评论。我总是愿意进行优化;而且我完全意识到可能有更好的方法来做我刚才推荐的事情。
如果我的上述解决方案对您不起作用,您能否描述一下您的具体情况以及您在使用 Android 时看到的结果WebView
?
最后,我已将此答案启用为“社区 wiki”,因此任何人和每个人都可以随时进行调整。
谢谢!
编辑:
使用最新的 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();
它将强制重绘。
这样你就可以有选择性,而不是一直重绘整个页面,这很昂贵
对我来说,这个问题只发生在三星设备上。我可以通过禁用 WebView 的硬件加速来修复它:
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()
但这似乎不起作用。虽然我的解决方案可能会更好,但它很简单并且对我有用(我只是显示了一个 twitter 登录)
另请参阅我在WebView 的答案在触及 Android 4.2.2 之前无法呈现,这个想法是将@Olivier 的函数导出到 JavaScript,这样您就可以在合理的部分触发 JS 的无效...我的上帝,又是一个黑客!!:)
我遇到了这个问题,因为 onPageStared 和 onPageFinished 我显示和隐藏了加载动画。
由于正在注入 JS,我从原始网站执行我的菜单和内容,并注意到在 onPageFinished 上注入 JS 会强制 webview 绘制内容。这是您可以在 onPageFinished 末尾添加的示例
view.loadUrl("javascript:(function() { var select = document.getElementsByClassName('something')[0]\r\n" +
" if(select)" +
" select.style.display = 'none';})()");
只需在 oncreate() 中清除 webView 的缓存,它就对我有用。
webView.clearCache(true);
您应该通过修改清单来启用硬件:
<application
...
android:hardwareAccelerated="true">
在你的活动 onResume()
webView.postDelayed(() -> {
if (!isFinishing() && webView != null) {
webView.setLayerType(View.LAYER_TYPE_NONE, null);
}
}, 1000);
在你的 onPause()
webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);