You need to enable JavaScript to run this app. Android中如何使用Java将Recyclerview内容转换为PDF文件?
优惠活动
大模型
产品
解决方案
定价
更多

Android中如何使用Java将Recyclerview内容转换为PDF文件?

Android Java实现RecyclerView转PDF的完整方案

1. 前置权限配置

先在AndroidManifest.xml中添加存储相关权限:

<!-- Android 12及以下存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- Android 13及以上媒体文件读取权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" />

Android 6.0及以上版本需要在代码中动态申请上述权限,获取用户授权后再执行PDF生成逻辑。

2. 核心实现逻辑

2.1 获取RecyclerView全部条目的拼接Bitmap

由于RecyclerView只会渲染可见区域的条目,需要手动遍历所有条目测量并拼接为完整Bitmap:

private Bitmap getRecyclerViewFullBitmap(RecyclerView recyclerView) {
    RecyclerView.Adapter<?> adapter = recyclerView.getAdapter();
    if (adapter == null) return null;
    int itemCount = adapter.getItemCount();
    int totalHeight = 0;
    Bitmap fullBitmap = null;
    Canvas canvas = null;

    for (int i = 0; i < itemCount; i++) {
        // 创建并绑定ViewHolder
        int viewType = adapter.getItemViewType(i);
        RecyclerView.ViewHolder holder = adapter.createViewHolder(recyclerView, viewType);
        adapter.onBindViewHolder(holder, i);
        // 测量Item尺寸
        holder.itemView.measure(
                View.MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), View.MeasureSpec.EXACTLY),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
        );
        holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(), holder.itemView.getMeasuredHeight());
        // 生成单条目的Bitmap
        holder.itemView.setDrawingCacheEnabled(true);
        holder.itemView.buildDrawingCache();
        Bitmap itemBitmap = Bitmap.createBitmap(holder.itemView.getDrawingCache());
        holder.itemView.setDrawingCacheEnabled(false);

        // 初始化总Bitmap
        if (fullBitmap == null) {
            fullBitmap = Bitmap.createBitmap(
                    holder.itemView.getMeasuredWidth(),
                    holder.itemView.getMeasuredHeight() * itemCount,
                    Bitmap.Config.ARGB_8888
            );
            canvas = new Canvas(fullBitmap);
        }
        // 把单条目Bitmap绘制到总Bitmap上
        canvas.drawBitmap(itemBitmap, 0, totalHeight, null);
        totalHeight += holder.itemView.getMeasuredHeight();
        itemBitmap.recycle();
    }
    return fullBitmap;
}

2.2 将Bitmap写入PDF文件

使用Android系统自带的PdfDocument类实现PDF生成,无需引入第三方依赖:

private void saveBitmapToPdf(Bitmap sourceBitmap, File outputFile) throws IOException {
    PdfDocument pdfDocument = new PdfDocument();
    // 创建和Bitmap尺寸匹配的PDF页面
    PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(
            sourceBitmap.getWidth(),
            sourceBitmap.getHeight(),
            1
    ).create();
    PdfDocument.Page page = pdfDocument.startPage(pageInfo);
    // 把Bitmap绘制到PDF页面
    page.getCanvas().drawBitmap(sourceBitmap, 0, 0, null);
    pdfDocument.finishPage(page);
    // 写入到输出文件
    FileOutputStream fos = new FileOutputStream(outputFile);
    pdfDocument.writeTo(fos);
    // 资源释放
    pdfDocument.close();
    fos.close();
    sourceBitmap.recycle();
}

2.3 生成PDF输出文件路径

适配不同Android版本的存储规则:

private File getPdfOutputFile() throws IOException {
    String fileName = "RecyclerView内容_" + System.currentTimeMillis() + ".pdf";
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // Android 10及以上保存到公共下载目录
        ContentValues values = new ContentValues();
        values.put(MediaStore.Downloads.DISPLAY_NAME, fileName);
        values.put(MediaStore.Downloads.MIME_TYPE, "application/pdf");
        values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
        Uri fileUri = getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
        if (fileUri == null) throw new IOException("文件创建失败");
        return new File(getRealPathFromUri(fileUri));
    } else {
        // 低版本直接操作本地存储
        File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
        if (!downloadDir.exists()) downloadDir.mkdirs();
        return new File(downloadDir, fileName);
    }
}

// Uri转本地路径辅助方法
private String getRealPathFromUri(Uri uri) {
    String path = null;
    Cursor cursor = getContentResolver().query(uri, new String[]{MediaStore.MediaColumns.DATA}, null, null, null);
    if (cursor != null && cursor.moveToFirst()) {
        path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA));
        cursor.close();
    }
    return path;
}

2.4 调用入口

建议在子线程执行转换逻辑,避免阻塞主线程:

public void startConvert(RecyclerView targetRecyclerView) {
    new Thread(() -> {
        try {
            Bitmap fullBitmap = getRecyclerViewFullBitmap(targetRecyclerView);
            if (fullBitmap == null) {
                runOnUiThread(() -> Toast.makeText(this, "列表内容获取失败", Toast.LENGTH_SHORT).show());
                return;
            }
            File pdfFile = getPdfOutputFile();
            saveBitmapToPdf(fullBitmap, pdfFile);
            runOnUiThread(() -> Toast.makeText(this, "PDF已保存到下载目录", Toast.LENGTH_LONG).show());
        } catch (Exception e) {
            e.printStackTrace();
            runOnUiThread(() -> Toast.makeText(this, "生成失败:" + e.getMessage(), Toast.LENGTH_SHORT).show());
        }
    }).start();
}

3. 优化建议

  • 若RecyclerView条目数量过多,拼接后的单Bitmap过大可能触发OOM,可按照A4纸标准尺寸分页渲染,每渲染满一页就新增一个PDF页面。
  • 若条目包含网络图片,需等图片加载完成后再生成对应条目的Bitmap,避免出现空白区域。
  • 可以添加PDF水印、页脚页码等自定义内容,直接在PdfDocument的Canvas上绘制即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 06:09:00