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
相关产品推荐
相关产品推荐

