
本文提供一种不依赖 iText、避免低质截图的原生 Android 方案,通过预测量所有 RecyclerView 条目并逐页渲染到 PrintedPdfDocument,实现文字清晰、布局可控、多页自适应的 PDF 导出。
本文提供一种不依赖 itext、避免低质截图的原生 android 方案,通过预测量所有 recyclerview 条目并逐页渲染到 `printedpdfdocument`,实现文字清晰、布局可控、多页自适应的 pdf 导出。
在 Android 开发中,将 RecyclerView 内容导出为 PDF 是一个常见但易踩坑的需求。许多开发者最初尝试“截屏转图”方式(如 Bitmap.createBitmap() + Canvas.draw()),结果发现:图像模糊、文字锯齿、缩放失真、无法选中文本——这本质上是将矢量内容强行降级为位图,违背了 PDF 的核心优势。
更优解是:绕过截图,直接复用 RecyclerView 的数据与视图逻辑,在 PDF 页面上“重绘”结构化内容。Android 自 API 19 起提供的 PrintedPdfDocument 正是为此而生——它底层基于 Skia 图形引擎,支持原生矢量绘制,无需引入 iText 等第三方库(规避许可证风险与体积膨胀),且能完美保留字体清晰度与排版精度。
✅ 核心思路:脱离“可见视图”,回归“数据驱动渲染”
RecyclerView 的设计哲学是“按需复用”,仅渲染屏幕可见项;而 PDF 导出需要全量、确定性、分页可控的绘制流程。因此关键步骤如下:
-
获取完整数据源(如
List<educationitem></educationitem>),而非依赖RecyclerView当前 ViewHolder; -
预计算每条目所需高度(使用
ViewGroup.measure()+ViewGroup.layout()模拟测量); - 按 A4 尺寸(595×842 pt)逐页填充内容,动态累加高度,超限则新建页面;
-
在每页 Canvas 上,用
TextView/Paint等原生组件逐条绘制(非截图,纯矢量)。
? 示例:教育经历列表导出为 PDF(精简版)
// 1. 创建 PDF 文档(A4,72dpi)
PrintedPdfDocument document = new PrintedPdfDocument(this,
new PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.ISO_A4)
.setResolution(new PrintAttributes.Resolution("res", "res", 72, 72))
.build());
// 2. 获取数据(确保非空且已排序)
List<educationitem> items = MyApplication.getEducationList();
// 3. 预设字体与边距(适配 PDF 分辨率)
Paint textPaint = new Paint();
textPaint.setTextSize(12f); // PDF 字号应比 UI 小(UI 常用 16sp+)
textPaint.setColor(Color.BLACK);
float margin = 40f;
float y = margin;
// 4. 分页绘制
for (int i = 0; i 842 - margin) {
// 结束当前页,新建一页
PdfDocument.Page page = document.startPage(new PrintedPdfDocument.PageInfo.Builder(595, 842, ++currentPage).create());
y = margin;
}
// 在当前页 Canvas 上绘制
PdfDocument.Page page = document.getPage(currentPage);
Canvas canvas = page.getCanvas();
// 绘制标题(矢量文本,无损缩放)
canvas.drawText(item.getTitle(), margin, y, textPaint);
y += lineHeight;
// 绘制学校、时间等字段(同理)
canvas.drawText(item.getSchool(), margin, y, textPaint);
y += lineHeight;
}
// 5. 完成并保存
document.finishPage(page);
File file = new File(getExternalFilesDir(null), "education_report.pdf");
try (FileOutputStream fos = new FileOutputStream(file)) {
document.writeTo(fos);
} catch (IOException e) {
Log.e("PDF", "Write failed", e);
}
document.close();</educationitem>
⚠️ 注意事项与最佳实践
-
务必后台执行:
RecyclerView全量测量与绘制可能耗时数百毫秒,必须放在WorkManager、Coroutine或AsyncTask中,避免主线程阻塞; -
字体嵌入问题:Android 默认字体(如 Roboto)在 PDF 中可正常显示,但若使用自定义
.ttf,需通过Typeface.createFromFile()加载并手动绘制Path—— 复杂场景建议改用iText7(需商业授权); -
图片处理:若条目含
ImageView,不可直接 draw,应先 decode 为Bitmap并用canvas.drawBitmap()缩放至合适 DPI(推荐 144dpi 以平衡清晰度与体积); -
分页健壮性:实际项目中需封装
PageBuilder类,支持自动分页、页眉页脚、表格线绘制等,GitHub 示例项目RecyclerviewPdf提供完整实现参考; -
兼容性提示:
PrintedPdfDocument仅支持 API 19+,旧版本需降级为iText或放弃 PDF 功能。
? 总结:RecyclerView 导出 PDF 的本质,不是“把列表拍张照”,而是“用 PDF 的画布,重新讲一遍数据的故事”。坚持数据驱动、预测量、分页渲染三原则,即可获得专业级、可搜索、高保真的 PDF 报告。










