SkiaSharp如何实现设备无关绘制 解决跨分辨率绘制位置偏移
跨设备显示偏差的核心原因
你遇到的位置偏移、线宽不一致问题,本质是SkiaSharp画布默认的坐标系统单位为物理像素,数值大小和设备屏幕密度直接绑定。
比如第一台测试设备的像素密度缩放比为2,你写的120的线宽、150的起点X坐标,对应逻辑单位下的60宽度、75X值;换到缩放比为3的设备上,同样120的物理像素值只对应40的逻辑宽度、50的X值,自然会出现线细、位置偏移的问题。
实现设备无关绘制的落地步骤
- 第一步:获取当前设备的像素缩放比
SkiaSharp.Views.Forms的画布参数中,args.Info返回的是物理像素维度的画布尺寸,而对应SKCanvasView控件的CanvasSize属性,返回的是和Xamarin.Forms一致的设备无关单位(DIU,即平台原生逻辑像素单位)维度的尺寸,两者的比值就是当前设备的像素缩放系数:var canvasView = (SKCanvasView)sender; float scale = info.Width / canvasView.CanvasSize.Width; - 第二步:统一坐标系统为逻辑单位,避免逐个值手动换算
不需要给每个硬编码的物理像素值单独做换算,直接给画布设置缩放矩阵,把整个坐标系统映射为DIU单位,后续所有绘制逻辑直接用和UI层一致的逻辑值计算即可:
设置完成后,你传入// 在canvas.Clear()之后、绘制内容之前设置 canvas.SetMatrix(SKMatrix.MakeScale(scale, scale));DrawLine、设置StrokeWidth的所有坐标/尺寸值,都会被自动按设备密度缩放为对应物理像素,不需要手动乘以scale。 - 第三步:替换原代码中的硬编码物理值
你原代码中写死的120的StrokeWidth、150/1300的X坐标,都是物理像素值,需要替换为逻辑单位下的对应值:
比如你希望线的宽度是逻辑单位下15,就把StrokeWidth从120改成15;希望画线的起点X是逻辑坐标下的100、终点X是画布逻辑宽度减去100的位置,就不要硬编码150和1300,改成用逻辑尺寸计算,适配不同屏幕宽度:SKPaint thickLinePaint = new SKPaint { Style = SKPaintStyle.Stroke, Color = SKColors.Yellow.WithAlpha(0x60), StrokeWidth = 15 // 原硬编码的120物理像素,替换为逻辑单位值 }; // 原有行位置计算逻辑保留,只要传入的高度值统一为逻辑单位即可 int positionY = (int)SepratorTypeHeight.PageHeader + (lineInPage * (int)SepratorTypeHeight.LineSeparator); float startX = 100; float endX = canvasView.CanvasSize.Width - 100; // 用逻辑宽度计算终点,适配不同屏幕宽度 thickLinePaint.StrokeCap = SKStrokeCap.Square; canvas.DrawLine(startX, positionY, endX, positionY, thickLinePaint); - 第四步:修正原有尺寸定义的单位
你代码中用到的SepratorTypeHeight.PageHeader、SepratorTypeHeight.LineSeparator如果是按第一台设备的物理像素定义的,需要统一改为逻辑单位存储,从根源避免跨设备偏差。
常见注意事项
- 不要直接用
args.Info的物理宽高作为参考值写死坐标,比如硬编码1300作为线的终点X,在更窄的屏幕上可能直接超出画布边界,优先用控件的逻辑尺寸按比例计算坐标点 - 每次
PaintSurface触发重绘时,要记得重新计算scale值、重置画布矩阵,避免因为页面切换、横竖屏旋转导致的缩放值不匹配 - 如果后续要做平移、旋转等其他变换,可以在已设置的缩放矩阵基础上叠加,不要直接重置矩阵覆盖原有缩放设置
内容的提问来源于stack exchange,提问作者Rauf
相关产品推荐
相关产品推荐

