[貝聊科技] WebView截長圖解決方案

貝聊科技發表於2017-11-07

作者:Windin  貝聊移動開發部  Android工程師
前言:本文主要講述了以下兩個方面:

  1. 普通WebView如何擷取長圖
  2. 針對X5核心中WebView如何擷取長圖

日常開發中,遇到為WebView擷取長圖算是一種常見的需求。網上聰明的程式設計師們提供了多種擷取WebView長圖的方法,這為我們的開發提供了很多便利。現在,也有很多APP是整合了X5核心的,網上對於X5核心的截長圖方案介紹比較少,所以這裡我整理了對WebView擷取長圖的比較通用可行的方法,並且對使用了x5核心的WebView的截圖方法進行分享。

一、普通WebView截長圖方案

普通WebView擷取長圖,這裡是指專案中沒有整合X5核心的情況。利用Google文件上的api可以順利截圖。以Android5.0為版本分界線,截圖採用不同的處理方式。

1. Android5.0以下版本

    /**
     * 對WebView進行截圖,雖然使用過期方法,但在當前Android版本中測試可行
     *
     * @param webView
     * @return
     */
    private static Bitmap captureWebViewKitKat(WebView webView) {
            Picture picture = webView.capturePicture();
            int width = picture.getWidth();
            int height = picture.getHeight();
            if (width > 0 && height > 0) {
                Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
                Canvas canvas = new Canvas(bitmap);
                picture.draw(canvas);
                return bitmap;
            }
            return null;
        }
    }複製程式碼

2. Android5.0及以上版本

在Android5.0及以上版本,Android對WebView進行了優化,為了減少記憶體使用和提高效能,使用WebView載入網頁時只繪製顯示部分。如果我們不做處理,仍然使用上述程式碼截圖的話,就會出現只截到螢幕內顯示的WebView內容,其它部分是空白的情況。
這時候,我們通過呼叫WebView.enableSlowWholeDocumentDraw()方法可以關閉這種優化,但要注意的是,該方法需要在WebView例項被建立前就要呼叫,否則沒有效果。所以我們在WebView例項被建立前加入程式碼:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        android.webkit.WebView.enableSlowWholeDocumentDraw();
    }複製程式碼

根據Google文件中描述,capturePicture()方法已不鼓勵使用,推薦我們通過webViewonDraw(Canvas)去獲取影像,所以這裡我們去拿到網頁的寬高後,就呼叫webView.draw(Canvas)方法生成webView截圖。

    private void captureWebViewLollipop(WebView webView) {
        float scale = webView.getScale();
        int width = webView.getWidth();
        int height = (int) (webView.getContentHeight() * scale + 0.5);
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        webView.draw(canvas);
        return bitmap;
    }複製程式碼

二、X5核心擷取長圖

使用X5核心擷取長圖有兩種方法,並且都可以不用考慮版本問題,這為我們提供了方便。在X5核心下,如果使用WebViewonDraw(Canvas)方法,會出現或多或少的問題,所以對這個方法棄坑了。以下是兩個截圖方法:

1. 使用X5核心方法snapshotWholePage(Canvas, boolean, boolean)

在X5核心中提供了一個擷取整個WebView介面的方法snapshotWholePage(Canvas, boolean, boolean),但是這個方法有個缺點,就是不以螢幕上WebView的寬高截圖,只是以WebViewcontentWidthcontentHeight為寬高截圖,所以截出來的圖片會不怎麼清晰,但作為縮圖效果還是不錯了。

    private static Bitmap captureX5WebViewUnsharp(Context context, WebView webView) {
        if (webView == null) {
            return null;
        }
        if (context == null) {
            context = webView.getContext();
        }
        int width = webView.getContentWidth();
        int height = webView.getContentHeight();
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        webView.getX5WebViewExtension().snapshotWholePage(canvas, false, false);
        return bitmap;
    }複製程式碼

2. 使用capturePicture()擷取清晰長圖

如果想要在X5核心下截到清晰的長圖,不能使用snapshotWholePage(),依然可以採用capturePicture()。X5核心下使用capturePicture()進行截圖,可以直接拿到WebView的清晰長圖,但這是個Deprecated的方法,使用的時候要做好異常處理。

三、總結

以上是WebView截長圖方法的總結和分享,對X5核心的截圖也是嘗試了多種途徑最後找到滿意的解決方案。另外,截長圖會佔用大量記憶體,容易觸發OOM,所以程式碼中也要注意對OOM的處理。

在使用了X5核心的專案中,使用WebView擷取長圖的判斷邏輯可以是:

// 有x5核心沒有生效,並且Android版本是5.0及以上時,呼叫enableSlowWholeDocumentDraw()方便擷取長圖
    if (!isX5Enabled() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        android.webkit.WebView.enableSlowWholeDocumentDraw();
    }
/* 建立WebView ×/

...

// 網頁截圖
    public void captureWholePage() {
        try {
            Bitmap bitmap = captureWebView();
            /* 對拿到的bitmap根據需要進行處理 */
        } catch (OutOfMemoryError oom) {
            /* 對OOM做處理
        }
    }複製程式碼

相關文章