2016-12-21 14 views
2

HTMLフラグメントをWebViewにレンダリングしてから、そのビットマップを取得したいと考えています。Android WebViewオフスクリーン/ビットマップレンダリングの問題

「ビットマップ」が最初に作成され
final WebView view = new WebView(this); 
String summary = "<html><body>You scored <b>192</b> points.</body></html>"; 
view.loadData(summary, "text/html", null); 
Bitmap bitmap= Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888); 
Canvas offscreen = new Canvas(bitmap); 
view.draw(offscreen); 

、それが透明である:ここでは(MainActivity.onCreateの内部にある)コード断片です。 'view.draw'呼び出し中は、すべて白の背景が提供されますが、HTMLサマリー文字列はレンダリングされません!どちらの場合も

final WebView view = new WebView(this); 
String summary = "<html><body>You scored <b>192</b> points.</body></html>"; 
view.loadData(summary, "text/html", null); 
Picture picture = new Picture(); 
Canvas picCanvas = picture.beginRecording(400, 400); 
view.draw(picCanvas); 
picture.endRecording(); 
Bitmap bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888); 
Canvas offscreen = new Canvas(bitmap); 
offscreen.drawPicture(picture); 

が、根底にあるビットマップが全白の背景を表示するように変更されていますが、他には何も:

まったく同じ効果が従うことによって得られていません。誰がなぜこのようなことが起こっているのか、いくつか明言することができますか?この問題が発生した場合、Android 6.0.1 API 23を実行しているSony Experia Z3を使用しています。

答えて

1

これを理解するにはしばらく時間がかかりましたが、ここに行きます。問題は、WebView.loadData()とWebView.loadURL()がノンブロッキングであることです。つまり、自分で作業を実行するのではなく、現在のアクティビティ(UIスレッド)にRunnableをポストする実際にHTMLを読み込んでレンダリングします。上記のようにActivity.onCreae()の中でWebView.loadData()を呼び出すことは、onCreate()が終了するまでloadData()の実際の作業が行われないため動作しません。したがって、空白の画像。

ここに提案された解決策があります。これは、メインApplicationオブジェクトのサブクラスに実装されています。

「これは」アプリケーションのサブクラスのオブジェクトを返す
public Bitmap renderHtml(final String html, int containerWidthDp, int containerHeightDp) { 
    final int containerWidthPx = dpToPx(containerWidthDp); 
    final int containerHeightPx = dpToPx(containerHeightDp); 

    final Bitmap bitmap = Bitmap.createBitmap(containerWidthPx, containerHeightPx, Bitmap.Config.ARGB_8888); 
    final CountDownLatch signal = new CountDownLatch(1); 
    final AtomicBoolean ready = new AtomicBoolean(false); 

    final Runnable renderer = new Runnable() { 
     @Override 
     public void run() { 
      final WebView webView = new WebView(getPane()); 
      webView.setWebChromeClient(new WebChromeClient() { 
       @Override 
       public void onProgressChanged(WebView v, int newProgress) { 
        super.onProgressChanged(v, newProgress); 
        if (newProgress==100) { 
         ready.set(true); 
        } 
       } 
      }); 
      webView.setPictureListener(new WebView.PictureListener() { 
       @Override 
       public void onNewPicture(WebView v, Picture picture) { 
        if (ready.get()) { 
         final Canvas c = new Canvas(bitmap); 
         v.draw(c); 
         v.setPictureListener(null); 
         signal.countDown(); 
        } 
       } 
      }); 
      webView.setWebViewClient(new WebViewClient() { 
       @Override 
       public void onPageFinished(WebView v, String url) { 
        super.onPageFinished(v, url); 
        ready.set(true); 
       } 
      }); 
      webView.layout(0, 0, containerWidthPx, containerHeightPx); 
      /* The next statement will cause a new task to be queued up 
       behind this one on the UI thread. This new task shall 
       trigger onNewPicture(), and only then shall we release 
       the lock. */ 
      webView.loadData(html, "text/html; charset=utf-8", null); 
     } 
    }; 

    runOnUiThread(renderer); 
    try { 
     signal.await(1000, TimeUnit.MILLISECONDS); 
    } catch (InterruptedException e) { 
    } 

    return bitmap; 
} 

、およびgetPaneは()現在実行活動を返します。それは私自身の考えをインターネットの通過重そうに歩くと試してみるの累積結果です。ルーチンがUIスレッドで実行されてはならないことに注意してください(または、上記のデッドロックと同じデッドロックが発生します)。主な洞察は、(1)loadData()呼び出しを含むrunOnUIThread Runnableが完了し、(2)loadData()呼び出しによって生成されたRunnableも完了している間待機するためにロックを使用することです(onNewPicture ()フックが呼び出されます)。 onNewPicture()の中で、完成したピクチャをビットマップにレンダリングし、ロックを解除して実行を続行します。

私はこの実装が、適切にレンダリングされたビットマップがほとんどの時間返されるという点で「信頼できる」ものであることを発見しました。私はまだどのイベントがloadData/loadURLによって起動されるのか完全に理解していません。これに関する一貫性はないようです。とにかく、signal.await()コールは1秒のタイムアウトを持ち、フォワード進行が保証されます。

関連する問題