lolyoshi
lolyoshi

Reputation: 1536

Android Webview: Detect when rendering is finished

I want to snapshot the WebView after the WebView is loaded. However, the returned bitmap is always null because the render hasn't loaded completed even though I use onPageFinished. I search on Internet and people suggest to use WebView.PictureListener, but this function is deprecated in API 12.

Some codes

public class MainActivity extends Activity {
    private WebView mButterflyWebView;

    /**
     * Gets html content from the assets folder.
     */
    private String getHtmlFromAsset() {
        InputStream is;
        StringBuilder builder = new StringBuilder();
        String htmlString = null;
        try {
            is = getAssets().open(getString(R.string.butterfly_html));
            if (is != null) {
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(is));
                String line;
                while ((line = reader.readLine()) != null) {
                    builder.append(line);
                }

                htmlString = builder.toString();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return htmlString;
    }

    /**
     * Initializes views, controls...
     */ 

    private void init() {
        mButterflyWebView = (WebView) findViewById(R.id.butterfly_webview);

        mButterflyWebView.setWebChromeClient(new WebChromeClient() {
        public void onProgressChanged(WebView view, int newProgress) {


            if (newProgress == 100){
                if (capturePictureWebView() != null){
                    saveBitmapToFile(capturePictureWebView());
                }
            }
        }
    });

    }

    /**
     * Loads html page with the content.
     */
    private void loadHtmlPage() {
        String htmlString = getHtmlFromAsset();
        if (htmlString != null)
            mButterflyWebView.loadDataWithBaseURL(
                    "file:///android_asset/images/", htmlString, "text/html",
                    "UTF-8", null);

        else
            Toast.makeText(this, R.string.no_such_page, Toast.LENGTH_LONG)
                    .show();
    }

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        init();
        loadHtmlPage();
    }

    private Bitmap capturePictureWebView() {
        mButterflyWebView.measure(MeasureSpec.makeMeasureSpec(
                MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED),
                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        mButterflyWebView.layout(0, 0, mButterflyWebView.getMeasuredWidth(),
                mButterflyWebView.getMeasuredHeight());
        mButterflyWebView.setDrawingCacheEnabled(true);
        mButterflyWebView.buildDrawingCache();

        if (mButterflyWebView.getMeasuredWidth() == 0 || mButterflyWebView.getMeasuredHeight() == 0){
            return null;
        }
        Bitmap bm = Bitmap.createBitmap(720, 1280, Bitmap.Config.ARGB_8888);

        System.out.println("width=" + mButterflyWebView.getMeasuredWidth());
        System.out.println("height=" + mButterflyWebView.getMeasuredHeight());
        Canvas bigcanvas = new Canvas(bm);
//        bigcanvas.scale(720/mButterflyWebView.getMeasuredWidth(), 1280/mButterflyWebView.getMeasuredHeight());
        Paint paint = new Paint();
        int iHeight = bm.getHeight();
        bigcanvas.drawBitmap(bm, 0, iHeight, paint);
        mButterflyWebView.draw(bigcanvas);
        return bm;
    }

    private void saveBitmapToFile(Bitmap bitmap) {
        try {

            FileOutputStream out = new FileOutputStream("/storage/sdcard0/a.png");
            bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Upvotes: 25

Views: 30400

Answers (7)

Cemil AKAN
Cemil AKAN

Reputation: 118

Kotlin solution

First Solution

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val webView : WebView = findViewById(R.id.webView)
    webView.webViewClient = MyWebViewClient()
 }

private class MyWebViewClient : WebViewClient() {
    override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
        println("Load Started")
    }
    override fun onPageFinished(view: WebView, url: String) {
        println("Load Finished")
    }
  }
}

Second Solution

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val webView : WebView = findViewById(R.id.webView)
    webView.webViewClient = object : WebViewClient() {
        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
            println("Load Started")
        }
        override fun onPageFinished(view: WebView, url: String) {
            println("Load Finished")
        }
     }
 }

Both solution actually same. So onPageStarted function run when page started to load in the other hang onPageFinished function works when page loaded entirely. You may want to write your as an example loadFinishedRun() function inside onPageFinished funtion.

Upvotes: -2

shreedhar
shreedhar

Reputation: 51

The renderer will not finish rendering when the OnPageFinshed method is called or the progress reaches 100% so both methods don't guarantee you that the view was completely rendered.

But you can figure out from OnLoadResource method what has been already rendered and what is still rendering. And this method gets called several times.

        @Override
        public void onLoadResource(WebView view, String url) {
            super.onLoadResource(view, url);
           // Log and see all the urls and know exactly what is being rendered and visible. If you wanna know when the entire page is completely rendered, find the last url from log and check it with if clause and implement your logic there.
            if (url.contains("assets/loginpage/img/ui/forms/")) {
                // loginpage is rendered and visible now.
               // your logic here.

            }
        }

Upvotes: 0

Daniel Novak
Daniel Novak

Reputation: 2756

The best way to detect if a page has rendered is to use the onPageCommitVisible callback, available from API 23. onPageLoadFinished is not suitable, since it's delivered too soon (when the HTML is processed, but not yet rendered).

webview.setWebViewClient(new WebViewClient() {

    @Override
     public void onPageCommitVisible (WebView view, 
            String url)
    }

}

Upvotes: 11

Frank
Frank

Reputation: 12300

You can do it like this, in your WebView:

@Override
public void invalidate() {
    super.invalidate();

    if (getContentHeight() > 0) {
        // WebView has displayed some content and is scrollable.
    }
}

Thanks to: https://stackoverflow.com/a/14678910/1310343

Upvotes: 5

lolyoshi
lolyoshi

Reputation: 1536

Finally I got the very detailed for this problem. This explains why it always returns null bitmap.

Every change in my app are triggered by a user action so I came up with a modified version that only trigger invalidate() after a touchEvent

Thank for all your help.

Android WebView renders blank/white, view doesn't update on css changes or HTML changes, animations are choppy

Upvotes: 2

Martin
Martin

Reputation: 4816

private class CfdWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        Log.v(TAG, "shouldOverrideUrlLoading: " + url);;
        return true;
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        Log.v(TAG, "onPageFinished: " + url);

        // do something
    }
}

Upvotes: -3

Andros
Andros

Reputation: 4069

You should try to use a WebChromeClient and implement onProgressChanged :

http://developer.android.com/reference/android/webkit/WebChromeClient.html#onProgressChanged(android.webkit.WebView, int)

mButterflyWebView.setWebChromeClient(new WebChromeClient() {

        @Override
        public void onProgressChanged(WebView view, int progress) {
           if (progress == 100) {
               // do screenshot
           }
        }
});

EDIT : to check if onPageStarted is loaded more than once :

mButterflyWebView.setWebViewClient(new WebViewClient() {

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
           Log.d("WebView", "onPageStarted " + url);
        }

        @Override
        public void onPageFinished(WebView view, String url) {
           Log.d("WebView", "onPageFinished " + url);
        }
    });

Upvotes: 12

Related Questions