Android使用PrintedPdfDocument和WebView生成PDF出现空白页问题
WebView生成PDF空白页的解决方法
问题根源
- 直接创建的WebView未加入视图层级,无法完成完整渲染流程,
onPageFinished触发时内容可能还没实际绘制出来。 - 用
webView.draw()直接绘制Canvas的方式不稳定,Android有专门的WebView打印API,应该优先用这个。 - 手动指定的页面尺寸和PrintAttributes不匹配,导致内容无法正常显示。
修复后的代码实现
fun createPdfFromHtml( context: Context, htmlContent: String, onSuccess: (File) -> Unit, onFail: (Throwable) -> Unit ) { // 创建WebView并设置布局参数 val webView = WebView(context).apply { settings.javaScriptEnabled = true layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) } // 创建临时布局承载WebView,确保能正常渲染 val tempLayout = FrameLayout(context).apply { addView(webView) // 根据上下文类型添加到视图层级 if (context is Activity) { context.window.decorView.rootView.addView(this) } else { val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager val params = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY else WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT ) windowManager.addView(this, params) } } webView.webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) val printAttributes = PrintAttributes.Builder() .setMediaSize(PrintAttributes.MediaSize.ISO_A4) .setResolution(PrintAttributes.Resolution("RES_A4", "A4 Resolution", 300, 300)) .setColorMode(PrintAttributes.COLOR_MODE_COLOR) .setMinMargins(PrintAttributes.Margins.NO_MARGINS) .build() // 使用WebView官方打印适配器生成PDF val printAdapter = webView.createPrintDocumentAdapter("HTML_PDF") val pdfFile = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "output.pdf" ) printAdapter.onLayout(null, printAttributes, null, object : PrintDocumentAdapter.LayoutResultCallback() { override fun onLayoutFinished(info: PrintDocumentInfo?, changed: Boolean) { printAdapter.onWrite( arrayOf(PageRange.ALL_PAGES), ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_CREATE or ParcelFileDescriptor.MODE_WRITE_ONLY), null, object : PrintDocumentAdapter.WriteResultCallback() { override fun onWriteFinished(pages: Array<out PageRange>?) { super.onWriteFinished(pages) // 清理临时视图 removeTempView(context, tempLayout) onSuccess(pdfFile) } override fun onWriteFailed(error: CharSequence?) { super.onWriteFailed(error) removeTempView(context, tempLayout) onFail(Exception(error?.toString() ?: "PDF生成失败")) } } ) } override fun onLayoutFailed(error: CharSequence?) { super.onLayoutFailed(error) removeTempView(context, tempLayout) onFail(Exception(error?.toString() ?: "布局失败")) } }, null) } } webView.loadDataWithBaseURL(null, htmlContent, "text/html", "UTF-8", null) } // 提取清理临时视图的方法,简化代码 private fun removeTempView(context: Context, tempLayout: FrameLayout) { if (context is Activity) { context.window.decorView.rootView.removeView(tempLayout) } else { val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager windowManager.removeView(tempLayout) } }
关键修复点说明
- 把WebView加入视图层级:WebView必须附着在Window上才能完成完整渲染,通过临时FrameLayout将WebView加入到Activity或全局WindowManager中,确保内容能正确绘制。
- 用官方PrintDocumentAdapter:WebView自带的
createPrintDocumentAdapter是专门用来打印和生成PDF的API,比直接画Canvas稳定得多,能正确处理分页、样式渲染等问题。 - 及时清理临时视图:生成PDF完成后移除临时添加的视图,避免内存泄漏。
- 权限提醒:Android 10及以下需要添加存储权限,Android 11及以上需要用MediaStore或MANAGE_EXTERNAL_STORAGE权限访问下载目录;非Activity上下文添加视图时,需要
SYSTEM_ALERT_WINDOW权限。
内容的提问来源于stack exchange,提问作者nasibeyyubov
相关产品推荐
相关产品推荐

