Android应用点击按钮将ImageView与TextView内容生成PDF保存到本地的实现方法
实现Android端ImageView+TextView内容生成PDF并保存到文件管理器方案
前置说明
我们直接用Android系统自带的PdfDocumentAPI实现,不需要引入第三方依赖,兼容API 19及以上版本,存储逻辑适配分区存储规则,Android 10及以上无需申请存储权限即可让用户在文件管理器中看到生成的文件。
步骤1:添加清单权限
在AndroidManifest.xml中添加如下权限,仅针对低版本设备生效:
<!-- 仅Android 9及以下需要存储写入权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <!-- 如果你的ImageView加载的是设备外部存储的图片,Android13及以上需要加这个权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" />
步骤2:编写PDF生成+存储核心逻辑
Kotlin示例代码(按钮点击事件中调用)
btnGeneratePdf.setOnClickListener { // 1. 先获取两个控件的内容 // 获取ImageView的Bitmap val imageBitmap = imageView.drawToBitmap() // 获取TextView的文本内容 val textContent = textView.text.toString() // 2. 异步执行PDF生成+存储操作,避免阻塞主线程 lifecycleScope.launch(Dispatchers.IO) { // 创建PdfDocument实例 val pdfDocument = PdfDocument() // 设置PDF页面参数,这里用A4纸尺寸,单位是PostScript点(1点=1/72英寸) val pageInfo = PdfDocument.PageInfo.Builder(595, 842, 1).create() val page = pdfDocument.startPage(pageInfo) val canvas = page.canvas // 3. 把图片画到PDF页面上,这里调整尺寸适配页面宽度,顶部留20点边距 val pageWidth = pageInfo.pageWidth val margin = 20f val imageMaxWidth = pageWidth - margin * 2 val scaleRatio = imageMaxWidth / imageBitmap.width val imageScaledHeight = imageBitmap.height * scaleRatio canvas.drawBitmap(imageBitmap, Matrix().apply { postScale(scaleRatio, scaleRatio) }, margin, margin, null) // 4. 把文本画到PDF页面上,图片下方留30点边距开始写文本 val textPaint = Paint().apply { textSize = 14f color = Color.BLACK isAntiAlias = true } var textY = margin + imageScaledHeight + 30f // 文本自动换行处理 val textLines = mutableListOf<String>() var currentLine = StringBuilder() textContent.forEach { char -> if (textPaint.measureText(currentLine.toString() + char) <= imageMaxWidth) { currentLine.append(char) } else { textLines.add(currentLine.toString()) currentLine.clear() currentLine.append(char) } } textLines.add(currentLine.toString()) // 逐行绘制文本 textLines.forEach { line -> canvas.drawText(line, margin, textY, textPaint) textY += textPaint.descent() - textPaint.ascent() + 5f // 行间距5点 } // 5. 完成页面绘制 pdfDocument.finishPage(page) // 6. 保存PDF到公共Download目录,文件管理器可见 val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, "生成的文件_${System.currentTimeMillis()}.pdf") put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) put(MediaStore.MediaColumns.IS_PENDING, 1) } } val resolver = contentResolver val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) uri?.let { fileUri -> resolver.openOutputStream(fileUri)?.use { outputStream -> pdfDocument.writeTo(outputStream) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { contentValues.clear() contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0) resolver.update(fileUri, contentValues, null, null) } // 切回主线程提示成功 launch(Dispatchers.Main) { Toast.makeText(this@YourActivity, "PDF已保存到Download目录", Toast.LENGTH_SHORT).show() } } // 关闭PdfDocument释放资源 pdfDocument.close() } }
Java版本核心逻辑差异
Java实现逻辑完全一致,仅语法不同:
- 控件转Bitmap可以用
Bitmap bitmap = Bitmap.createBitmap(imageView.getWidth(), imageView.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); imageView.draw(canvas);兼容低版本 - 异步操作可以用
AsyncTask或者Thread+Handler实现
注意事项
- API 23到API 28的设备需要动态申请
WRITE_EXTERNAL_STORAGE权限后才能执行存储逻辑 - 内容较多的情况下可以新增多个PDF页面,重复执行
startPage->绘制->finishPage的流程即可 - 可以根据需求调整PDF页面尺寸、边距、文本大小、图片位置等参数
内容的提问来源于stack exchange,提问作者vraj kalathiya
相关产品推荐
相关产品推荐

