From 11521e29bd4b762375c83f459142bf146d7ba9f1 Mon Sep 17 00:00:00 2001 From: xiaji Date: Mon, 2 Mar 2026 22:58:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8B=BC=E5=9B=BE=E5=BA=95?= =?UTF-8?q?=E9=83=A8=E6=A0=87=E9=A2=98=E5=92=8C=E5=86=85=E5=AE=B9=E5=8C=BA?= =?UTF-8?q?=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inspection/camera/ui/merge/MergeScreen.kt | 40 ++---- .../inspection/camera/util/ImageProcessor.kt | 115 +++++++++++++++++- 2 files changed, 123 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/inspection/camera/ui/merge/MergeScreen.kt b/app/src/main/java/com/inspection/camera/ui/merge/MergeScreen.kt index 29003d2..40a6159 100644 --- a/app/src/main/java/com/inspection/camera/ui/merge/MergeScreen.kt +++ b/app/src/main/java/com/inspection/camera/ui/merge/MergeScreen.kt @@ -365,20 +365,12 @@ fun MergeScreen( context, imageItems, layoutType, - imageQuality - ).let { bitmap -> - if (title.isNotBlank() || content.isNotBlank()) { - ImageProcessor.addTextToBitmap( - bitmap, - title, - content, - titleStyle, - contentStyle - ) - } else { - bitmap - } - } + imageQuality, + title, + content, + titleStyle, + contentStyle + ) } showPreview = true } @@ -439,20 +431,12 @@ fun MergeScreen( context, imageItems, layoutType, - imageQuality - ).let { mergedBitmap -> - if (title.isNotBlank() || content.isNotBlank()) { - ImageProcessor.addTextToBitmap( - mergedBitmap, - title, - content, - titleStyle, - contentStyle - ) - } else { - mergedBitmap - } - } + imageQuality, + title, + content, + titleStyle, + contentStyle + ) } val fileName = ImageProcessor.generateFileName(title.ifBlank { "合成" }) diff --git a/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt b/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt index 5ccd873..469e3e4 100644 --- a/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt +++ b/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt @@ -155,9 +155,13 @@ object ImageProcessor { context: Context, images: List, layoutType: MergeLayoutType, - quality: ImageQuality + quality: ImageQuality, + title: String = "", + content: String = "", + titleStyle: WatermarkStyle = WatermarkStyle.Default, + contentStyle: WatermarkStyle = WatermarkStyle.Default ): Bitmap { - if (images.isEmpty()) { + if (images.isEmpty() && title.isBlank() && content.isBlank()) { return Bitmap.createBitmap(1920, 1080, Bitmap.Config.ARGB_8888) } @@ -165,9 +169,20 @@ object ImageProcessor { val rows = layoutType.rows val outputWidth = 1920 - val outputHeight = 1080 val cellWidth = outputWidth / cols - val cellHeight = outputHeight / rows + val cellHeight = outputWidth / cols // 保持正方形格子 + + // 底部文字区域高度 + val textAreaHeight = if (title.isNotBlank() || content.isNotBlank()) { + 200 // 200像素高度的文字区域 + } else { + 0 + } + + // 图片区域高度 + val imageAreaHeight = cellHeight * rows + + val outputHeight = imageAreaHeight + textAreaHeight val result = Bitmap.createBitmap(outputWidth, outputHeight, Bitmap.Config.ARGB_8888) val canvas = Canvas(result) @@ -175,6 +190,7 @@ object ImageProcessor { val paint = Paint(Paint.ANTI_ALIAS_FLAG) + // 绘制图片网格 images.forEachIndexed { index, imageItem -> if (index >= rows * cols) return@forEachIndexed @@ -208,6 +224,97 @@ object ImageProcessor { } } + // 绘制底部文字区域 + if (textAreaHeight > 0) { + val textTop = imageAreaHeight + + // 绘制白色背景 + canvas.drawRect( + 0f, textTop.toFloat(), + outputWidth.toFloat(), outputHeight.toFloat(), + Paint().apply { color = Color.WHITE } + ) + + // 绘制分割线 + canvas.drawLine( + 0f, textTop.toFloat(), + outputWidth.toFloat(), textTop.toFloat(), + Paint().apply { + color = Color.LTGRAY + strokeWidth = 2f + } + ) + + // 绘制标题 + if (title.isNotBlank()) { + val titlePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + textSize = titleStyle.fontSize * 2f // 放大标题 + color = titleStyle.textColor.toArgb() + typeface = Typeface.DEFAULT_BOLD + textAlign = Paint.Align.CENTER + } + + // 标题背景 + val titleBgPaint = Paint().apply { + color = titleStyle.backgroundColor.toArgb() + } + val titleWidth = titlePaint.measureText(title) + val titleHeight = titlePaint.fontMetrics.let { it.descent - it.ascent } + val titleBgRect = RectF( + (outputWidth - titleWidth) / 2 - 20, + textTop + 20f, + (outputWidth + titleWidth) / 2 + 20, + textTop + 20f + titleHeight + 20 + ) + canvas.drawRoundRect(titleBgRect, 8f, 8f, titleBgPaint) + + // 标题文字 + canvas.drawText(title, outputWidth / 2f, textTop + 20f + titleHeight, titlePaint) + } + + // 绘制内容 + if (content.isNotBlank()) { + val contentPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + textSize = contentStyle.fontSize * 1.5f // 放大内容 + color = contentStyle.textColor.toArgb() + typeface = Typeface.DEFAULT + } + + val padding = 40f + val contentMaxWidth = outputWidth - padding * 2 + val contentLines = wrapText(content, contentPaint, contentMaxWidth) + val lineHeight = contentPaint.fontMetrics.let { it.descent - it.ascent } + + // 内容背景 + val contentBgPaint = Paint().apply { + color = contentStyle.backgroundColor.toArgb() + } + + // 从标题下方开始绘制内容 + val contentStartY = if (title.isNotBlank()) { + textTop + 80f + lineHeight + } else { + textTop + 20f + lineHeight + } + + val totalContentHeight = contentLines.size * lineHeight + 20 + val contentBgRect = RectF( + padding - 10, + contentStartY - lineHeight - 10, + outputWidth - padding + 10, + contentStartY + totalContentHeight + ) + canvas.drawRoundRect(contentBgRect, 8f, 8f, contentBgPaint) + + // 内容文字 + var y = contentStartY + contentLines.forEach { line -> + canvas.drawText(line, padding, y, contentPaint) + y += lineHeight + } + } + } + return result }