You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android使用PDF API绘制矢量Drawable拉伸失真问题求助

解决Android PDF API绘制矢量Drawable时的拉伸模糊问题

看起来你遇到的问题是因为直接给VectorDrawable设置了很小的Bounds,导致它被渲染成低分辨率的位图,然后绘制到PDF中就会出现拉伸模糊的情况——毕竟矢量图的优势是无限缩放不损失画质,但如果把它当成固定尺寸的位图来渲染,就浪费了这个特性。

下面是几个关键的修复思路和修改后的代码示例:

核心原因

VectorDrawable在调用setBounds()并绘制到Canvas时,如果Bounds尺寸远小于其固有尺寸,系统会将矢量图形栅格化成对应大小的位图,再进行绘制。而PDF本身是矢量格式,但这里绘制的是栅格化后的位图,自然会模糊。我们需要让VectorDrawable以矢量形式渲染,通过Canvas的缩放来适配目标尺寸,而不是直接限制它的Bounds。

解决方案步骤

  • 保持矢量图的固有尺寸,通过Canvas缩放适配目标区域:不要硬编码小尺寸的Bounds,而是利用Canvas的scale()方法来缩小矢量图,这样矢量图形会保持矢量特性渲染,不会丢失细节。
  • 保持宽高比,避免拉伸:计算目标区域的宽高比和矢量图的固有宽高比,取合适的缩放比例,确保图标不会被拉伸变形。
  • 适配PDF的DPI(可选但推荐):PDF的单位是“点”(1点 = 1/72 英寸),而Android设备通常使用160DPI(mdpi)作为基准。如果你的目标尺寸是基于Android的dp,需要转换为PDF的点:dpValue * (72f / 160f),这样在PDF中显示的大小会更符合预期。

修改后的代码示例

针对你代码中的moonIconstarIcon绘制部分,修改如下:

Drawable starIcon = getContext().getDrawable(R.drawable.ic_star);
VectorDrawableCompat moonIcon = VectorDrawableCompat.create(getContext().getResources(), R.drawable.ic_moon, getActivity().getTheme());
PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(595, 842, 2).create();
PdfDocument.Page page = pdfDocument.startPage(pageInfo);
Canvas canvas = page.getCanvas();

float x = drawX + (perItemWidth * col);
RectF rectF = new RectF();
rectF.set(x + 2.5F - perItemWidth, drawY + 1, x + 1, drawY + perItemHeight - 1);
float importantSpaceFromLeft = (rectF.width() * 10F) / 100F;
float importantSpaceFromTop = ((rectF.height() * 2F) / 100F);

// --- 处理moonIcon ---
Rect moonIconBounds = new Rect();
moonIconBounds.left = (int) (rectF.left + ((rectF.height() * 10F) / 100F)) + 10;
moonIconBounds.right = (int) (rectF.left + ((rectF.height() * 10F) / 100F)) + 20;
moonIconBounds.bottom = (int) (rectF.bottom - importantSpaceFromTop) - 10 - 2;
moonIconBounds.top = (int) (rectF.bottom - importantSpaceFromTop) - 20 - 2;

// 获取moonIcon的固有宽高(矢量图的原始尺寸)
int moonIntrinsicWidth = moonIcon.getIntrinsicWidth();
int moonIntrinsicHeight = moonIcon.getIntrinsicHeight();
if (moonIntrinsicWidth > 0 && moonIntrinsicHeight > 0) {
    // 计算目标尺寸和缩放比例,保持宽高比
    float targetMoonWidth = moonIconBounds.width();
    float targetMoonHeight = moonIconBounds.height();
    float scaleX = targetMoonWidth / moonIntrinsicWidth;
    float scaleY = targetMoonHeight / moonIntrinsicHeight;
    float moonScale = Math.min(scaleX, scaleY); // 避免拉伸

    canvas.save();
    // 平移到目标位置的左上角
    canvas.translate(moonIconBounds.left, moonIconBounds.top);
    // 缩放画布,让矢量图适配目标尺寸
    canvas.scale(moonScale, moonScale);
    // 设置Bounds为矢量图的固有尺寸,保证矢量渲染
    moonIcon.setBounds(0, 0, moonIntrinsicWidth, moonIntrinsicHeight);
    moonIcon.draw(canvas);
    canvas.restore();
}

// --- 处理starIcon ---
Rect starIconBounds = new Rect();
starIconBounds.left = (int) (rectF.left + ((rectF.height() * 10F) / 100F)) + 10;
starIconBounds.right = (int) (rectF.left + ((rectF.height() * 10F) / 100F)) + 20;
starIconBounds.bottom = (int) (rectF.bottom - importantSpaceFromTop);
starIconBounds.top = (int) (rectF.bottom - importantSpaceFromTop) - 10;

// 同样处理starIcon
if (starIcon instanceof VectorDrawableCompat || starIcon instanceof VectorDrawable) {
    int starIntrinsicWidth = starIcon.getIntrinsicWidth();
    int starIntrinsicHeight = starIcon.getIntrinsicHeight();
    if (starIntrinsicWidth > 0 && starIntrinsicHeight > 0) {
        float targetStarWidth = starIconBounds.width();
        float targetStarHeight = starIconBounds.height();
        float scaleX = targetStarWidth / starIntrinsicWidth;
        float scaleY = targetStarHeight / starIntrinsicHeight;
        float starScale = Math.min(scaleX, scaleY);

        canvas.save();
        canvas.translate(starIconBounds.left, starIconBounds.top);
        canvas.scale(starScale, starScale);
        starIcon.setBounds(0, 0, starIntrinsicWidth, starIntrinsicHeight);
        starIcon.draw(canvas);
        canvas.restore();
    }
} else {
    // 如果是普通Drawable(比如BitmapDrawable),按原方式处理
    starIcon.setBounds(starIconBounds);
    starIcon.draw(canvas);
}

额外检查点

  • 确认你的ic_staric_moon确实是VectorDrawable(后缀为.xml,内容包含<vector>标签),而不是被转换为BitmapDrawable。有些SVG转换工具可能会默认生成BitmapDrawable,这会直接导致模糊。
  • 检查VectorDrawable的viewportWidthviewportHeight是否设置合理,这是矢量图的坐标系统基准,不合理的设置也可能导致缩放异常。

这样修改后,矢量图会以矢量形式渲染到PDF中,即使尺寸很小也能保持清晰,不会出现拉伸模糊的问题。

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

火山引擎 最新活动