Android WebView屏幕外/位图渲染问题 [英] Android WebView offscreen/bitmap rendering issue

查看:157
本文介绍了Android WebView屏幕外/位图渲染问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将HTML片段呈现到WebView,然后获取它的位图.这是一个代码片段(位于MainActivity.onCreate内部):

I'd like to render an HTML fragment to a WebView and then grab a bitmap of it. Here is a code fragment (which is inside of 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摘要字符串!

When 'bitmap' is originally created, it is transparent. During the 'view.draw' call, it is provided with an all-white background, but the HTML summary string is not rendered!

可以通过以下方式获得完全相同的效果:

The exact same effect is obtained by the following:

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.

In both cases, the underlying bitmap is modified to show an all-white background, but nothing else. Can anyone shed some light on why this is happening? I'm using a Sony Experia Z3 running Android 6.0.1 API 23, if this matters.

推荐答案

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

It took me a while to figure this one out, but here goes. The problem is/was that WebView.loadData() and WebView.loadURL() are non-blocking -- they do not perform any work themselves, but instead post a Runnable to the current Activity (i.e., the UI thread) which, when executed, will actually load and render the HTML. So placing the WebView.loadData() call inside of Activity.onCreae(), as I did above, can never work since the actual work for loadData() will not occur until onCreate() has exited; hence the blank image.

这是建议的解决方案,在主要Application对象的子类中实现.这是通过互联网进行漫游并尝试自己的想法的累积结果:

Here's the proposed solution, implemented in a subclass of the main Application object. It is a cumulative result of trudging through the Internets and trying out my own ideas:

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()中,我们将完成的图片渲染到位图上,然后释放锁,以便继续执行.

where 'this' returns the Application subclass object, and getPane() returns the presently executed Activity. Note that the routine must NOT be executed in the UI thread (or we'd encounter the same deadlock as desribed above). The main insight here is to use a lock in order to wait while (1) the runOnUIThread Runnable containing the loadData() call completes, and then (2) the Runnable spawned by the loadData() call also completes (which is when the onNewPicture() hook is called). Inside onNewPicture(), we render the completed picture onto the bitmap and then release the lock so that execution can continue.

我发现此实现是可靠的",因为大多数情况下会返回正确渲染的位图.我仍然不完全了解loadData/loadURL触发了哪些事件;似乎没有任何一致性.无论如何,signal.await()调用的超时时间为1秒,因此可以保证前进.

I have found this implementation to be "reliable" in that a properly rendered bitmap is returned most of the time. I still do not completely understand which events get fired by loadData/loadURL; there does not seem be any consistency about this. At any rate, the signal.await() call has a timeout of 1 second so that forward progress is guaranteed.

这篇关于Android WebView屏幕外/位图渲染问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆