2016-12-21 1 views
2

Я хотел бы сделать фрагмент HTML в WebView, а затем захватить его растровое изображение. Ниже приведен фрагмент кода (который находится внутри MainActivity.onCreate):Проблема с веб-браузером Android Webscreen/bitmap

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); 

Когда «точечный рисунок» изначально создан, он является прозрачным. Во время вызова 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); 

В обоих случаях, лежащий в основе растрового изображения изменяется, чтобы показать все-белый фон, но ничего другого. Может ли кто-нибудь пролить свет на то, почему это происходит? Я использую Sony Experia Z3 под управлением Android 6.0.1 API 23, если это имеет значение.

ответ

1

Мне понадобилось время, чтобы понять это, но здесь идет. Проблема заключается в том, что WebView.loadData() и WebView.loadURL() не блокируются - они сами не выполняют какую-либо работу, а вместо этого отправляют Runnable в текущую активность (т.е. поток пользовательского интерфейса), который, когда выполненный, фактически загрузит и отобразит HTML. Поэтому размещение вызова WebView.loadData() внутри Activity.onCreae(), как я уже говорил выше, никогда не может работать, так как фактическая работа для loadData() не будет выполняться до выхода onCreate(); следовательно, пустое изображение.

Предлагаемое решение представлено в подклассе основного объекта 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; 
} 

где «это» возвращает Application объект подкласса и getPane() возвращает в настоящее время выполняется активность. Обратите внимание, что подпрограмма НЕ должна выполняться в потоке пользовательского интерфейса (или мы столкнулись с тем же тупиком, что и описано выше). Основное понимание здесь - использовать блокировку, чтобы ждать, пока (1) runOnUIThread Runnable, содержащий вызов loadData(), завершает, а затем (2) также запускает Runnable, порожденный вызовом loadData() (т.е. когда onNewPicture() называется). Внутри onNewPicture() мы делаем завершенное изображение на растровое изображение, а затем отпустите блокировку, чтобы выполнение продолжалось.

Я обнаружил, что эта реализация является «надежной» в том смысле, что правильно обработанное растровое изображение возвращается большую часть времени. Я до сих пор не совсем понимаю, какие события запускаются loadData/loadURL; похоже, в этом нет никакой согласованности. Во всяком случае, вызов signal.await() имеет тайм-аут в 1 секунду, чтобы гарантировать прогресс вперед.