Canvas.drawText()在不同尺寸Bitmap上绘制文字大小不一致如何解决
我正在使用Canvas API的drawText()方法在Bitmap*(图片转换得到的Bitmap)*上绘制文字,已经可以成功在Bitmap上绘制文字,但一直无法解决以下问题:
如果是设备相机拍摄的图片,叠加绘制的文字大小正常;但如果从图库中选择任意一张其他图片,叠加绘制的文字就会过大。
以下是Canvas绘制文字的相关代码:
val scale = context?.resources?.displayMetrics?.density val textSizeInPixels = 44f val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL color = Color.WHITE setShadowLayer(1f, 0f, 1f, Color.WHITE) textSize = textSizeInPixels * (scale ?: 1F) } val bounds = Rect() paint.getTextBounds(textToDraw, 0, textToDraw.length, bounds) //imagePath是相机拍摄或图库选择的图片路径 val bitmap = BitmapFactory.decodeStream(FileInputStream(File(imagePath))) val x = bitmap.width.minus(bitmap.width.times(widthScale)) val y = bitmap.height.minus(bitmap.height.times(heightScale)) canvas.drawText(textToDraw, x, y, paint)
经过调试发现,相机拍摄的图片尺寸为2176x3264 像素,在该尺寸图片上Canvas绘制的文字大小正常;而从图库选中的测试图片尺寸为914x1624 像素,在该图上绘制的文字尺寸明显过大。
核心问题
我该如何保证无论图片尺寸如何,绘制的文字大小始终保持一致?
我需要不同来源的两张图片上绘制的文字大小完全相同。
效果说明
- 设备相机拍摄的图片,绘制文字大小正常
- 图库选中的图片,绘制文字过大
布局代码
以下是用来展示Bitmap的ImageView对应的XML布局代码:
<com.google.android.material.card.MaterialCardView android:id="@+id/cv_share_img" android:layout_width="match_parent" android:layout_height="@dimen/dimen_0" android:layout_marginStart="@dimen/dimen_20" android:layout_marginTop="@dimen/dimen_20" android:layout_marginEnd="@dimen/dimen_20" app:cardCornerRadius="@dimen/dimen_20" app:layout_constraintDimensionRatio="H,9:16" app:layout_constraintTop_toBottomOf="@id/iv_close"> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/iv_share_img" android:layout_width="match_parent" android:layout_height="match_parent" android:adjustViewBounds="true" android:scaleType="fitCenter" /> </com.google.android.material.card.MaterialCardView>
问题原因
当前的文字大小是基于屏幕密度计算的固定像素值,不会随Bitmap的尺寸变化调整。高分辨率的相机图片总像素多,固定像素的文字占比小,看起来大小正常;低分辨率的图库图片总像素少,同样像素大小的文字占比就会大很多,最终显示在相同大小的ImageView里时,就会出现图库图片文字过大的问题。
解决方法
提供两种可行方案,按需选择即可:
方案1:按图片尺寸比例动态计算文字大小
以正常显示的相机图片为基准,计算出合理的文字大小和图片高度的比例,后续所有图片都用这个比例计算文字大小:
// 基准比例可根据实际显示效果调整,示例值基于相机图高度3264px、正常文字大小88px计算得到88/3264≈0.027 val TEXT_HEIGHT_RATIO = 0.027f val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL color = Color.WHITE setShadowLayer(1f, 0f, 1f, Color.WHITE) // 用当前Bitmap的高度乘以比例得到适配的文字大小 textSize = bitmap.height * TEXT_HEIGHT_RATIO }
方案2:先统一缩放Bitmap到目标尺寸再绘制
你的ImageView尺寸是固定9:16比例的,可以先把所有加载的Bitmap统一缩放到和ImageView的实际像素大小一致,再在缩放后的Bitmap上绘制固定大小的文字,这样不管原图多大,绘制出来的文字大小完全一致:
// 先获取ImageView的实际尺寸(可在View渲染完成后通过view.post{}获取) val targetWidth = iv_share_img.width val targetHeight = iv_share_img.height // 把原图缩放成目标尺寸 val scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, targetWidth, targetHeight, true) // 之后在scaledBitmap对应的Canvas上绘制文字,textSize用原有固定值即可 val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL color = Color.WHITE setShadowLayer(1f, 0f, 1f, Color.WHITE) textSize = 44f * (scale ?: 1F) }
内容的提问来源于stack exchange,提问作者iCantC

