如何匹配Android Canvas与A3纸张尺寸并添加等距网格背景?
Android Canvas 导出A3 PDF与等距网格背景实现方案
一、导出A3尺寸PDF是否需要先定义Canvas为A3尺寸?
不需要直接将用户交互用的Canvas定义为A3尺寸,但导出PDF时必须基于A3规格创建专用的PDF Canvas,并做好绘制内容的缩放适配,具体流程如下:
- 保持交互Canvas与屏幕尺寸匹配,让用户正常完成绘制操作,同时将用户绘制的所有路径(Path)或图形数据保存到集合中(比如
List<Path>)。 - 计算A3尺寸的像素值:A3物理规格为297mm×420mm,若要保证打印清晰度,建议采用300dpi分辨率,计算得像素尺寸为
3508×4961;若仅用于屏幕预览,可使用72dpi,对应842×1191。 - 创建PDF文档并生成A3页面,将保存的用户绘制内容缩放后绘制到PDF Canvas上:
// 计算A3像素尺寸(300dpi) val a3WidthMm = 297f val a3HeightMm = 420f val dpi = 300f val widthPx = (a3WidthMm * dpi / 25.4).toInt() val heightPx = (a3HeightMm * dpi / 25.4).toInt() // 创建PDF文档 val pdfDocument = PdfDocument() val pageInfo = PdfDocument.PageInfo.Builder(widthPx, heightPx, 1).create() val page = pdfDocument.startPage(pageInfo) val pdfCanvas = page.canvas // 缩放用户绘制内容到A3尺寸 val viewWidth = yourCustomView.width.toFloat() val viewHeight = yourCustomView.height.toFloat() val scaleX = widthPx / viewWidth val scaleY = heightPx / viewHeight pdfCanvas.scale(scaleX, scaleY) // 绘制用户保存的所有路径 val userPaint = Paint().apply { color = Color.BLACK strokeWidth = 5f style = Paint.Style.STROKE } yourSavedPaths.forEach { path -> pdfCanvas.drawPath(path, userPaint) } // 完成PDF生成并保存 pdfDocument.finishPage(page) pdfDocument.writeTo(FileOutputStream(File(getExternalFilesDir(null), "a3_drawing.pdf"))) pdfDocument.close()
二、实现等距网格背景绘制
等距网格由两组呈60°/120°夹角的平行线交叉构成,可在自定义View的onDraw方法中先绘制网格背景,再叠加用户绘制内容,具体实现:
class IsometricDrawView(context: Context, attrs: AttributeSet?) : View(context, attrs) { private val gridPaint = Paint().apply { color = Color.parseColor("#E0E0E0") strokeWidth = 1f style = Paint.Style.STROKE } private val userPaths = mutableListOf<Path>() private val userPaint = Paint().apply { color = Color.BLACK strokeWidth = 3f style = Paint.Style.STROKE isAntiAlias = true } private val gridCellSize = 40f // 网格单元边长,可按需调整 override fun onDraw(canvas: Canvas) { super.onDraw(canvas) // 先绘制等距网格背景 drawIsometricGrid(canvas) // 再绘制用户绘制的路径 userPaths.forEach { path -> canvas.drawPath(path, userPaint) } } private fun drawIsometricGrid(canvas: Canvas) { val viewWidth = width.toFloat() val viewHeight = height.toFloat() val angle60 = Math.toRadians(60.0).toFloat() val sin60 = sin(angle60) val cos60 = cos(angle60) // 绘制第一组平行线(与水平方向成60°) var yOffset = 0f val stepY = gridCellSize * sin60 while (yOffset <= viewHeight + stepY) { val startX = -yOffset / tan(angle60) val endX = viewWidth + (viewHeight - yOffset) / tan(angle60) canvas.drawLine(startX, yOffset, endX, viewHeight, gridPaint) yOffset += stepY } // 绘制第二组平行线(与水平方向成120°) var xOffset = 0f val stepX = gridCellSize * cos60 while (xOffset <= viewWidth + stepX) { val startY = -xOffset * tan(angle60) val endY = viewHeight + (viewWidth - xOffset) * tan(angle60) canvas.drawLine(xOffset, startY, xOffset, endY, gridPaint) xOffset += stepX } } // 添加用户绘制路径的方法 fun addUserPath(path: Path) { userPaths.add(path) invalidate() } }
如果View支持滚动,可通过监听滚动偏移量,调整网格绘制的起始坐标,保证网格随滚动连续显示。
内容的提问来源于stack exchange,提问作者Amir
相关产品推荐
相关产品推荐

