web-dev-qa-db-ja.com

Android WebViewが空白/白でレンダリングされ、CSSの変更またはHTMLの変更時にビューが更新されず、アニメーションが途切れる

CSSクラスに変更を加えたとき、変更が常に表示されるとは限りません。これは、ボタンにダウンクラス名のようなものを追加するタッチイベントがある場合に発生するようです。ボタンは更新されず、ページ上の他の何も更新されません。いつ機能するかは非常に一定ではありません。また、コンテンツが何もない状態で要素が白く表示されることもあります。これは非常にイライラします!

51
Kyle

私はカイルのソリューションを実装し、問題を解決しました。しかし、アプリを開いたときに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を使用してプログラミングを行ったことがないため、これを行うにはより良い解決策があるかもしれません。

17
Olivier

注:
Android 4.4+以降では、より良いソリューションがあります。 CrossWalk と呼ばれるドロップインWebViewの置き換えです。最新のChromium-kitを使用しており、素晴らしいです。あなたはそれについてここで読むことができます: 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" />

Updatebuild.gradleの値を変更する必要があります

次に、WebViewに表示されるもののプライマリCSSファイルに、次のようなものを追加します。

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

持っている他の要素タイプをbody 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);
}

2番目に重要ですが、私は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を使用している場合、最後のステップが1つあります。 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でどのような結果が得られているかを説明してください。

最後に、この回答を "community wiki" として有効にしたため、誰でも誰でも気軽に調整できます。

ありがとう!


編集:

最新のPhoneGapでは、init()メソッドを次のようにする必要があります。

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

re:再描画の問題。要素からプロパティを読み取ることで再描画を強制できます。

あなたはこれを行うと言います:

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

後でこれを行う場合:

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

再描画を強制します。

この方法では、ページ全体を常に再描画する代わりに選択的にすることができます。これは高価です

8
Andrew Bullock

私にとって、この問題はSamsungデバイスでのみ発生していました。 WebViewsのハードウェアアクセラレーションを無効にすることで修正できました。

webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

それが役に立てば幸い。

8

上記および他の場所で指摘したように、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ログインを表示しているだけです)

6
Dori

OnPageStaredとonPageFinishedがロードアニメーションの表示と非表示を切り替えるため、この問題が発生しました。

JSをインジェクトしていたので、元のWebサイトのメニューや項目を実行し、onPageFinishedにJSをインジェクトすると、Webビューがコンテンツを描画するように強制されることに気付きました。 onPageFinishedの最後に追加できるものの例を次に示します

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

Oncreate()でwebViewのキャッシュをクリアするだけで機能します。

webView.clearCache(true);
0
Ankush Chugh

WebViewがタッチされるまでレンダリングに失敗するAndroid 4.2.2 で私の回答も参照してください賢明な部分で...私の神、さらに別のハック!! :)

0
rupps