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

使用Xamarin开发Android:Canvas中图片自动缩放问题咨询

问题分析与解决方案

首先,咱们来拆解你遇到的核心问题:图片在xhdpi模拟器上自动缩放是Android资源加载机制的默认行为,并非什么"后台隐式操作"——只是你可能没注意到Drawable资源适配的细节,而文档其实是有明确说明的。下面一步步给你讲清楚:

为什么图片会自动缩放?

Android的资源系统会根据当前设备的屏幕密度,自动缩放Drawable资源来保证视觉一致性。具体来说:

  • 每个屏幕密度对应一个密度因子:mdpi是基准(因子1.0),xhdpi的因子是2.0,也就是说xhdpi屏幕的像素密度是mdpi的2倍。
  • 当你从默认的drawable目录加载图片时,Android会把这张图当作mdpi资源处理。在xhdpi设备上,系统会自动把它放大2倍(32×32 → 64×64),这样图片在屏幕上的视觉大小和在mdpi设备上一致。
  • 而mdpi设备的密度因子是1.0,所以图片保持原始尺寸显示,这就是为什么你在mdpi模拟器上看到的是正常的32×32。

你之前查阅的dp相关文档,是针对布局尺寸的适配(比如控件的宽高用dp),而Drawable资源的缩放是另一个维度的适配规则,这部分在"支持不同屏幕密度"的文档章节里有详细说明,可能你之前没关联到一起。

你的代码里的额外问题

除了资源加载的默认行为,你的代码还有两个会加重缩放问题的点:

  1. partialBitmap的尺寸错误:
    你用Resources.Configuration.ScreenWidthDp和ScreenHeightDp来创建Bitmap,但Bitmap.CreateBitmap需要的是像素值,而ScreenWidthDp是dp单位。比如xhdpi设备上,360dp的宽度对应720像素,但你直接用360作为宽度创建Bitmap,导致这个Bitmap本身尺寸偏小,后续绘制到屏幕时会被拉伸,图片自然看起来更大。
  2. OnDraw中错误回收Bitmap:
    OnDraw方法会频繁调用(比如屏幕刷新、View重绘时),你在这里调用partialBitmap.Recycle();会直接销毁Bitmap,下次绘制时就会报错,这是严重的内存问题。

解决步骤

1. 让图片保持原始像素尺寸

要避免系统自动缩放图片,把你的32×32图片放在drawable-nodpi目录下(如果没有这个目录,就在Resources下新建)。这个目录的资源不会被系统缩放,无论什么密度的设备,都会以原始像素尺寸加载。

2. 修正partialBitmap的创建逻辑

先正确获取屏幕的像素尺寸,再创建Bitmap:

public MyCanvasPath(Context context) : base(context) //constructor
{
    // 先初始化DisplayMetrics获取密度因子
    DisplayMetrics dm = new DisplayMetrics();
    ((Activity)context).WindowManager.DefaultDisplay.GetMetrics(dm);
    float scale = dm.Density;

    // 把dp转换为像素
    int screenWidthPx = (int)(Resources.Configuration.ScreenWidthDp * scale);
    int screenHeightPx = (int)(Resources.Configuration.ScreenHeightDp * scale);

    partialBitmap = Bitmap.CreateBitmap(screenWidthPx, screenHeightPx, Bitmap.Config.Argb8888);

    // 加载图片(现在放在drawable-nodpi,不会被缩放)
    jp1 = BitmapFactory.DecodeResource(Resources, Resource.Drawable.jp1);
    jp2 = BitmapFactory.DecodeResource(Resources, Resource.Drawable.jp2);
    jp3 = BitmapFactory.DecodeResource(Resources, Resource.Drawable.jp3);
    jp4 = BitmapFactory.DecodeResource(Resources, Resource.Drawable.jp4);

    Canvas myCanvas = new Canvas(partialBitmap);
    b = myCanvas.Height;
    a = myCanvas.Width;
    bw = jp1.Width; // 现在bw应该是32,不会再是64
    IList<Bitmap> pImgList = new List<Bitmap> { jp1, jp2, jp3, jp4 };
    imgCount = pImgList.Count;
    for (int x = 0; x < imgCount; x++)
    {
        myCanvas.DrawBitmap(pImgList.ElementAt(x), bw * x, 0, null);
    }
}

3. 修正Bitmap的回收时机

把partialBitmap.Recycle();移到View的销毁方法中,比如重写OnDetachedFromWindow:

protected override void OnDetachedFromWindow()
{
    base.OnDetachedFromWindow();
    if (partialBitmap != null && !partialBitmap.IsRecycled)
    {
        partialBitmap.Recycle();
        partialBitmap = null;
    }
    // 顺便回收其他图片资源
    foreach(var bmp in new List<Bitmap>{jp1,jp2,jp3,jp4})
    {
        if(bmp != null && !bmp.IsRecycled)
        {
            bmp.Recycle();
        }
    }
}

总结

  • 图片自动缩放是Android资源适配的默认行为,根源是资源目录的密度匹配规则,文档中是有明确说明的(只是和dp的适配规则是分开的)。
  • 通过将图片放在drawable-nodpi目录,可以强制保持原始像素尺寸。
  • 修正Bitmap的创建和回收逻辑,避免额外的拉伸和内存问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:49:35