You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android含ScrollView的RelativeLayout完整转PDF问题求助

解决ScrollView嵌套ListView生成PDF仅显示部分内容的问题

我之前也碰到过一模一样的问题——ScrollView里套ListView,直接拿getDrawingCache生成Bitmap转PDF时,ListView只会渲染当前屏幕可见的item,而且整个布局的高度测量也不对,导致PDF只显示部分内容。下面给你几个可行的解决思路,按推荐程度排序:

方法一:改用iText直接构建PDF(最推荐)

用Bitmap转PDF不仅容易出现内容截断,还会有分辨率低、文件大的问题。直接用iText的API把每个部分的内容手动添加到PDF里,既灵活又可靠。

步骤:

  1. 先修正你代码里的路径错误(原来的pdfFolder + timeStamp + ".pdf"会导致路径拼接错误,应该用File.separator分隔)
  2. 用iText的Document、PdfPTable、Paragraph等组件逐个添加发票的各个部分:商家信息、客户信息、产品表头、产品列表、总计等。

示例代码:

private void createPdfWithiText() throws IOException, DocumentException {
    File pdfFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "pdfdemo");
    if (!pdfFolder.exists()) {
        pdfFolder.mkdirs(); // 用mkdirs确保多级目录都能创建
    }
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
    // 正确拼接路径
    File myFile = new File(pdfFolder, timeStamp + ".pdf");
    OutputStream output = new FileOutputStream(myFile);

    // 初始化PDF文档,用A4纸
    Document document = new Document(PageSize.A4);
    PdfWriter.getInstance(document, output);
    document.open();

    // 1. 添加商家信息
    Paragraph businessName = new Paragraph("Business Name", FontFactory.getFont(FontFactory.HELVETICA_BOLD, 16));
    businessName.setAlignment(Element.ALIGN_CENTER);
    document.add(businessName);
    document.add(new Paragraph("Business Address", FontFactory.getFont(FontFactory.HELVETICA, 14)));
    document.add(new Paragraph("Business contactNo", FontFactory.getFont(FontFactory.HELVETICA, 14)));
    // 添加黑色分隔线
    document.add(new LineSeparator(3f, 100f, BaseColor.BLACK, Element.ALIGN_CENTER, -1));
    document.add(new Paragraph("\n")); // 换行

    // 2. 添加客户信息,用表格排版更整齐
    PdfPTable customerTable = new PdfPTable(2);
    customerTable.setWidthPercentage(100);
    customerTable.addCell(new Phrase("GSTIN:", FontFactory.getFont(FontFactory.HELVETICA_BOLD)));
    customerTable.addCell(new Phrase("xyz1234566", FontFactory.getFont(FontFactory.HELVETICA)));
    customerTable.addCell(new Phrase("Bill date:", FontFactory.getFont(FontFactory.HELVETICA_BOLD)));
    customerTable.addCell(new Phrase("20-12-2017", FontFactory.getFont(FontFactory.HELVETICA)));
    document.add(customerTable);
    document.add(new LineSeparator(3f, 100f, BaseColor.BLACK, Element.ALIGN_CENTER, -1));
    document.add(new Paragraph("\n"));

    // 3. 添加产品表头
    PdfPTable productHeader = new PdfPTable(3);
    productHeader.setWidthPercentage(100);
    productHeader.addCell(new Phrase("Product Name", FontFactory.getFont(FontFactory.HELVETICA_BOLD, 14)));
    productHeader.addCell(new Phrase("GST%", FontFactory.getFont(FontFactory.HELVETICA_BOLD, 14)));
    productHeader.addCell(new Phrase("Total", FontFactory.getFont(FontFactory.HELVETICA_BOLD, 14)));
    document.add(productHeader);
    document.add(new LineSeparator(3f, 100f, BaseColor.BLACK, Element.ALIGN_CENTER, -1));

    // 4. 添加产品列表
    PdfPTable productTable = new PdfPTable(3);
    productTable.setWidthPercentage(100);
    for (ItemModel item : itemsList) {
        productTable.addCell(new Phrase(item.getProductName(), FontFactory.getFont(FontFactory.HELVETICA, 12)));
        productTable.addCell(new Phrase(String.valueOf(item.getGstPercent()), FontFactory.getFont(FontFactory.HELVETICA, 12)));
        productTable.addCell(new Phrase(String.valueOf(item.getTotal()), FontFactory.getFont(FontFactory.HELVETICA, 12)));
    }
    document.add(productTable);
    document.add(new LineSeparator(3f, 100f, BaseColor.BLACK, Element.ALIGN_CENTER, -1));
    document.add(new Paragraph("\n"));

    // 5. 添加总计
    Paragraph netTotal = new Paragraph("Net Total: " + invoiceTotal, FontFactory.getFont(FontFactory.HELVETICA_BOLD, 14));
    netTotal.setAlignment(Element.ALIGN_CENTER);
    document.add(netTotal);

    document.close();
    // 生成后可以打开PDF
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(Uri.fromFile(myFile), "application/pdf");
    intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivity(intent);
}

方法二:修复Bitmap生成逻辑,确保完整渲染布局

如果你坚持要用Bitmap转PDF,需要先手动计算ListView的总高度,强制它渲染所有item,再重新测量整个ScrollView的高度。

步骤:

  1. 写一个方法计算ListView的总高度(包括所有item和分割线)
  2. 在生成Bitmap前,设置ListView的高度为计算出的总高度
  3. 重新测量并布局整个根布局,再生成Bitmap

示例代码:

// 计算ListView总高度的工具方法
private int getListViewTotalHeight(ListView listView) {
    ListAdapter adapter = listView.getAdapter();
    if (adapter == null || adapter.getCount() == 0) return 0;

    int totalHeight = 0;
    // 遍历所有item,计算每个item的高度
    for (int i = 0; i < adapter.getCount(); i++) {
        View listItem = adapter.getView(i, null, listView);
        // 测量item的高度
        listItem.measure(
            View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.EXACTLY),
            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
        );
        totalHeight += listItem.getMeasuredHeight();
    }
    // 加上分割线的总高度
    totalHeight += listView.getDividerHeight() * (adapter.getCount() - 1);
    // 加上ListView的padding
    totalHeight += listView.getPaddingTop() + listView.getPaddingBottom();
    return totalHeight;
}

// 修改你的createPdf方法
private void createPdf() throws IOException, DocumentException {
    File pdfFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "pdfdemo");
    if (!pdfFolder.exists()) pdfFolder.mkdirs();
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
    File myFile = new File(pdfFolder, timeStamp + ".pdf");
    OutputStream output = new FileOutputStream(myFile);

    // 先处理ListView,强制显示所有item
    int listTotalHeight = getListViewTotalHeight(listView);
    ViewGroup.LayoutParams listParams = listView.getLayoutParams();
    listParams.height = listTotalHeight;
    listView.setLayoutParams(listParams);

    // 重新测量根布局的完整高度
    main.setDrawingCacheEnabled(true);
    // 强制测量布局的完整尺寸
    main.measure(
        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
    );
    main.layout(0, 0, main.getMeasuredWidth(), main.getMeasuredHeight());
    main.buildDrawingCache(true);

    Bitmap b = Bitmap.createBitmap(main.getDrawingCache());
    main.setDrawingCacheEnabled(false);

    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    b.compress(Bitmap.CompressFormat.PNG, 100, bytes); // 用PNG比JPEG更清晰,没有压缩失真
    Image myImg = Image.getInstance(bytes.toByteArray());

    // 调整图片适应PDF页面,这里用A4纸
    Document document = new Document(PageSize.A4);
    PdfWriter.getInstance(document, output);
    document.open();
    // 缩放图片到A4纸宽度
    myImg.scaleToFit(document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin(), document.getPageSize().getHeight());
    myImg.setAlignment(Element.ALIGN_CENTER);
    document.add(myImg);
    document.close();

    // 打开PDF
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(Uri.fromFile(myFile), "application/pdf");
    intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivity(intent);
}

额外注意事项

  1. 先修正你XML布局里的语法错误:
    • 有一处<TextView <TextView的标签重复,要删除多余的标签
    • <View android:id="@+id/v4"标签没有闭合,要加上/>
  2. 安卓10及以上版本访问外部存储需要申请MANAGE_EXTERNAL_STORAGE权限,或者用MediaStore来保存文件,避免权限问题
  3. 用iText的时候,要确保依赖的版本正确,比如在build.gradle里添加:
    implementation 'com.itextpdf:itextg:5.5.10'
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:29:12