【问题标题】:Android WebView offscreen/bitmap rendering issueAndroid WebView 离屏/位图渲染问题
【发布时间】:2016-12-21 23:54:51
【问题描述】:

我想将 HTML 片段渲染到 WebView,然后抓取它的位图。这是一个代码片段(在 MainActivity.onCreate 内部):

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

在这两种情况下,底层位图都被修改为显示全白背景,但仅此而已。任何人都可以阐明为什么会这样吗?如果这很重要,我正在使用运行 Android 6.0.1 API 23 的 Sony Experia Z3。

【问题讨论】:

    标签: android android-webview android-bitmap


    【解决方案1】:

    我花了一段时间才弄清楚这一点,但就这样吧。问题是/是 WebView.loadData() 和 WebView.loadURL() 是非阻塞的——它们自己不执行任何工作,而是将 Runnable 发布到当前 Activity(即 UI 线程),当执行,将实际加载和呈现 HTML。因此,正如我在上面所做的那样,将 WebView.loadData() 调用放在 Activity.onCreae() 中是永远无法工作的,因为 loadData() 的实际工作在 onCreate() 退出之前不会发生;因此是空白图像。

    这是建议的解决方案,在主应用程序对象的子类中实现。这是在互联网上跋涉并尝试自己的想法的累积结果:

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

    其中'this'返回Application子类对象,getPane()返回当前执行的Activity。请注意,例程不能在 UI 线程中执行(否则我们会遇到与上述相同的死锁)。这里的主要见解是使用锁来等待(1)包含 loadData()调用的 runOnUIThread Runnable 完成,然后(2)由 loadData()调用产生的 Runnable 也完成(这是当 onNewPicture () 钩子被调用)。在 onNewPicture() 内部,我们将完成的图片渲染到位图上,然后释放锁,以便继续执行。

    我发现这个实现是“可靠的”,因为大部分时间都会返回正确渲染的位图。我仍然不完全理解 loadData/loadURL 触发了哪些事件;这似乎没有任何一致性。无论如何,signal.await() 调用的超时时间为 1 秒,这样可以保证前进的进度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多