1

I am rendering some very simple HTML (just some text and a small image) in a WebView off screen (not set as content view of an activity) so I can create a Bitmap from the content.

The way to know when the content is fully rendered I have based on this answer:

final AtomicBoolean rendered = new AtomicBoolean(false);
final WebView view = new WebView(this) {
    @Override
    public void invalidate() {
        if (getProgress() == 100 && getContentHeight() > 0) {
            if (! rendered.get()) {
                rendered.set(true);
                // Content should be fully rendered
            }
        }
        super.invalidate();
    }
};

// Load and lay out content
view.loadUrl(url);
view.setInitialScale(100);
view.layout(0, 0, 240, 420);

I've tested this successfully on 4.1.2, 4.2.2, 4.4.2 and 5.0. But with 4.0.3, it seems invalidate() is never called.

While trying all kinds of things I found out that showing a Toast and doing a delayed (1 sec.) call to invalidate() solves the problem:

@Override
public void onPageFinished(final WebView view, final String url) {
        Toast.makeText(MainActivity.this, "Page loaded", Toast.LENGTH_SHORT).show();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (! rendered.get()) {
                    view.invalidate();
                }
            }
        }, 1000);
}

Of course I don't consider this a valid solution, but while the delayed call to invalidate() somehow makes sense to me, I really wonder what side effect of the Toast does the trick here. Does it do some damage causing a redraw or something like that?

Community
  • 1
  • 1
Torsten Römer
  • 3,834
  • 4
  • 40
  • 53

2 Answers2

1

Sorry can not help you very much since I would like to make same operation of you (get screenshot from offline webview control) but in my case I'm not able to receive invalidate() event until the webview is offline. If I set as visible by inserting into the main window the invalidate() start to be generated but in case of offline (invisible) no invalidate() call is generated. The code you developed for get such result is only the snippet you posted here or there is some additional function to call for have the webview "active" in offline mode also?

About your problem what I can suggest is to override the other invalidate() definitions like:

public void invalidate(Rect dirty)
public void invalidate(int l, int t, int r, int b)

since in my tests I noted also these last was called.

Thank you

Suppaman
  • 151
  • 1
  • 12
  • I posted an answer with the approach I am using now, which is the best I could come up with. Thanks for the hint about the other `invalidate()` methods, I will have a look at that. – Torsten Römer Jan 10 '15 at 16:32
0

After a lot more testing on different (virtual) devices, this is the way that works reliable for me so far. Note that the "hack" for 4.0.3 mentioned in my original question still applies but is not included here.

Overriding invalidate() did not prove to be always reliable on all tested versions, so I am using PictureListener.onNewPicture(WebView view, Picture picture) even though it is deprecated.

In my activity:

final WebView view = new WebView(this);    

// Zooming in can improve font quality
final float scale = 2.0;
// Unfortunately, there is no method view.getContentWidth()
final int contentWidth = 240;

view.setPictureListener(new PictureListener() {

    @Override
    public void onNewPicture(final WebView view, final Picture picture) {
        if (view.getProgress() == 100 && view.getContentHeight() > 0) {
            view.setPictureListener(null);
            // Content is now fully rendered
            final int width = Math.round(contentWidth * scale);
            final int height = Math.round(view.getContentHeight() * scale);
            final Bitmap bitmap = getBitmap(view, width, height);
            // Display or print bitmap...
        }
    }
});

view.loadUrl(url);
view.setInitialScale(Math.round(scale * 100));
// Width and height must be at least 1
view.layout(0, 0, 1, 1);

And the getBitmap() method:

private Bitmap getBitmap(
        final WebView view, final int width, final int height) {
    final Bitmap bitmap = Bitmap.createBitmap(
            width, height, Bitmap.Config.ARGB_8888);
    final Canvas canvas = new Canvas(bitmap);
    view.draw(canvas);

    return bitmap;
}

This is the best I can offer. The app has not been tested extensively yet, but so far it never failed to correctly render a small document including a small GIF image.

Torsten Römer
  • 3,834
  • 4
  • 40
  • 53