You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android:不显示WebView时渲染HTML并生成指定尺寸Bitmap的问题

问题根因

  • 首次运行抛出尺寸异常:onPageFinished回调仅代表HTML资源加载完成,不代表页面内容渲染完成,首次触发时WebView还未完成布局测量,得到的宽高为0,创建Bitmap时就会触发尺寸必须大于0的异常。另外原有测量逻辑使用完全无约束的UNSPECIFIED模式,无法固定你需要的380宽度。
  • 生成Bitmap尺寸不符合预期:动态创建的WebView没有绑定正确的布局参数,setMinimumWidth在无父布局的场景下不会生效,且WebView默认会根据设备密度自动缩放内容,导致实际渲染宽度远小于预期的380。同时原有截图方法中存在多余的绘制逻辑,也会影响输出结果。

解决方案

1. 修改WebView初始化逻辑

在print方法中补充WebView渲染配置,固定宽度参数:

private void print() {
    // 先销毁旧WebView避免内存泄漏
    if (mReceiptView != null) {
        mReceiptView.destroy();
    }
    mReceiptView = new WebView(this);
    // 配置渲染属性
    WebSettings webSettings = mReceiptView.getSettings();
    webSettings.setJavaScriptEnabled(true); // 若HTML包含JS逻辑需开启
    webSettings.setUseWideViewPort(true);
    webSettings.setLoadWithOverviewMode(true);
    webSettings.setBuiltInZoomControls(false);
    webSettings.setDisplayZoomControls(false);
    // 禁用设备密度自动缩放,固定缩放比例100%
    webSettings.setDefaultZoom(WebSettings.ZoomDensity.FAR);
    mReceiptView.setInitialScale(100);

    mWebviewClient = new SnapshotWebviewClient(this);
    mReceiptView.setWebViewClient(mWebviewClient);

    // 绑定布局参数,固定宽度380,高度自适应
    mReceiptView.setLayoutParams(new ViewGroup.LayoutParams(
            380, // 若为dp单位需先转为像素,可使用dp转px工具方法
            ViewGroup.LayoutParams.WRAP_CONTENT
    ));

    String receiptHtml = getHtml();
    mReceiptView.loadData(receiptHtml, "text/html; charset=utf-8", "UTF-8");
}

如果你的380是dp单位,可添加以下工具方法转换为像素:

private int dp2px(int dp) {
    return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
}

将上述代码中的380替换为dp2px(380)即可。

2. 修复截图时机与截图逻辑

修改SnapshotWebviewClient中的onPageFinished方法,添加延迟等待渲染完成,同时修复screenShot方法中的错误逻辑:

@Override
public void onPageFinished(WebView view, String url) {
    Log.i(TAG, "SnapshotWebviewClient.onPageFinished");
    super.onPageFinished(view, url);
    // 延迟100ms等待页面渲染完成,避免首次宽高为0
    view.postDelayed(() -> {
        try {
            Bitmap bitmap = screenshot(view);
            Log.i(TAG, String.format("Bitmap size: w(%d), h(%d)", bitmap.getWidth(), bitmap.getHeight()));
            mMainActivity.ImagePreview.setImageBitmap(bitmap);

            java.io.File externalPath = view.getContext().getExternalFilesDir(null);
            java.io.File filePath = new java.io.File(externalPath, "Receipt.png");
            java.io.FileOutputStream stream = new java.io.FileOutputStream(filePath);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
            stream.close();
            Log.i(TAG, "WebViewClient.PrintToBitmap");
        } catch (Exception printErr) {
            Log.e(TAG, "PrintWebView: "+printErr.toString(), printErr);
        }
    }, 100);
}

private Bitmap screenshot(android.webkit.WebView webView) {
    // 宽度固定380精确测量,高度无约束自适应
    webView.measure(
            View.MeasureSpec.makeMeasureSpec(380, View.MeasureSpec.EXACTLY),
            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
    );
    Log.i(TAG, String.format("webView dimensions1: w(%d), h(%d)", webView.getMeasuredWidth(), webView.getMeasuredHeight()));
    webView.layout(0, 0, webView.getMeasuredWidth(), webView.getMeasuredHeight());
    Log.i(TAG, String.format("webView dimensions2: w(%d), h(%d)", webView.getMeasuredWidth(), webView.getMeasuredHeight()));
    
    Bitmap bitmap = Bitmap.createBitmap(webView.getMeasuredWidth(), webView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    // 直接让WebView绘制到画布上,删除原有多余的drawBitmap逻辑
    webView.draw(canvas);
    return bitmap;
}

内容的提问来源于stack exchange,提问作者Colin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 11:48:02