在 Canvas 上绘制自定义大小的文本
Drawing Custom Size Text on Canvas
我需要在 canvas 上画一些文字。我已经在 canvas 中添加了一个矩形框,我的目标是使该文本适合该框,但无论我尝试什么,文本都无法完美适合所有屏幕尺寸。
另一件事是这篇文章很简单HTML。
TextView currDateLabel = new TextView(getContext());
currDateLabel.layout(0, 0, boxSize.x, boxSize.y);
currDateLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.label_date));
currDateLabel.setTextColor(Color.WHITE);
currDateLabel.setText(Html.fromHtml("Dec 11<sup><small><small>TH</small></small></sup>"));
currDateLabel.setDrawingCacheEnabled(true);
Bitmap dateBitmap = currDateLabel.getDrawingCache(true);
// I've also tried setting the width of this Bitmap but it doesn't seem to do anything
this.canvas.drawBitmap(dateBitmap, boxPosition.x, boxPosition.y, null);
所以在这里解释一些变量:
boxSize
是一个 Point
对象,用于存储 canvas 上框的宽度和高度。 这些值似乎对 TextView
的大小没有影响,因为我可以看到文本超出框。
boxPosition
是一个 Point
对象,存储 x
和 y
框在 canvas.[=34 上的位置=]
R.dimen.label_date
是 dimens.xml
文件中的值。
我创建了多个 dimens.xml
文件(values/dimens.xml
、values-hdpi/dimens.xml
、values-xhdpi/dimens.xml
等)
我认为我可以测试大多数屏幕尺寸并计算出每个屏幕类别的文本尺寸。然而它并没有像我预期的那样工作。
这些值适用于 Samsung Galaxy S8:
Screen Width: 1440, Screen Height: 2768
Screen Density: 4.0
Screen Density DPI: 640
Screen Scaled Density: 4.0
这些值适用于 Samsung Galaxy S7:
Screen Width: 1440, Screen Height: 2560
Screen Density: 4.0
Screen Density DPI: 640
Screen Scaled Density: 4.0
它们都属于 XXXHDPI
类别,因此它们具有相同的字体大小,但 Galaxy S8 文本适合方框,而 Galaxy S7 文本太大而超出方框。
我是不是漏掉了什么?
有什么完全不同的方法可以让我在 Canvas 上绘制 HTML 文字吗?
============================================= =====
编辑:
更详细地解释我的用例:
我指的盒子是一个 SVG/Vector 对象。
label_down.xml
:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="45dp"
android:viewportWidth="90"
android:viewportHeight="45">
<path
android:fillColor="?attr/labelColor"
android:pathData="M 0 0 L 0 37.532 L 36.63 37.532 L 45 45 L 53.371 37.532 L 90 37.532 L 90 0 Z" />
</vector>
然后我从该向量创建一个 Bitamp
对象并在 Canvas 上绘制 Bitmap
。
VectorDrawableCompat vectorDrawableCompat = VectorDrawableCompat.create(getContext().getResources(), R.drawable.label_down, theme);
Bitmap bitmap = Bitmap.createBitmap(vectorDrawableCompat.getIntrinsicWidth(), vectorDrawableCompat.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
this.canvas.drawBitmap(bitmap, someXPos, someYPos, null);
这样做会导致这样的结果:
现在,我的目标是在该框内创建文本。
希望这更有意义。
如您所见,HTML 文本无法正确呈现,我建议将 DOM 对象绘制成 canvas
我认为这指出了 "sp" 和 "dp" 单位之间的区别。您没有在问题中记录尺寸文件中 label_date
的值。我假设,因为它是文本大小的单位,所以您使用的是 "sp" 单位。
如果是,请将 "xxsp" 更改为 "xxdp" 以检查您是否发现差异。我刚刚在 Nexus 5X API 26 模拟器和 Galaxy S7 运行 Android 7.0 上试过这个,"dp" 单元解决了你的 HTML 文本的问题。这是我看到的:
Nexus 5X API 26 模拟器
Galaxy S7 设备 API Android 7.0
更新: 我很好奇到底发生了什么。看起来可缩放像素 (SP) 的缩放因子大于 S7 设置中的一个。转到 "Settings->Display->Screen zoom and font" 并将 "FONT SIZE" 设置为第三个井号。您的文字现在应该可以正确显示了。
您不希望文本独立于图形进行缩放,因此,IMO,使用密度无关像素 (DP) 作为文本单位是保持图形和图形之间一致性的正确做法文字.
第二次更新:对于给定的屏幕size/density,只要你的图形大小是固定的,字体显示的大小可以根据设备设置,您将继续有文本不适合的可能性。如果您不认为将与密度无关的像素指定为字体的单位,您可以继续使用可缩放像素,但调整代码中使用的字体大小。像这样:
float fontScale = getResources().getConfiguration().fontScale;
textview.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources()
.getDimensionPixelSize(R.dimen.label_date_sp) / fontScale);
这会将字体的有效缩放系数调整回 1.0。这里的缺点是,虽然您可能指定 14sp
字体大小,但实际显示的大小会有所不同,并且会小于其他同样为 14sp
的元素。这与指定 "DP" 具有相同的效果,但将使用更标准的 "SP" 单位。
我需要在 canvas 上画一些文字。我已经在 canvas 中添加了一个矩形框,我的目标是使该文本适合该框,但无论我尝试什么,文本都无法完美适合所有屏幕尺寸。
另一件事是这篇文章很简单HTML。
TextView currDateLabel = new TextView(getContext());
currDateLabel.layout(0, 0, boxSize.x, boxSize.y);
currDateLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.label_date));
currDateLabel.setTextColor(Color.WHITE);
currDateLabel.setText(Html.fromHtml("Dec 11<sup><small><small>TH</small></small></sup>"));
currDateLabel.setDrawingCacheEnabled(true);
Bitmap dateBitmap = currDateLabel.getDrawingCache(true);
// I've also tried setting the width of this Bitmap but it doesn't seem to do anything
this.canvas.drawBitmap(dateBitmap, boxPosition.x, boxPosition.y, null);
所以在这里解释一些变量:
boxSize
是一个Point
对象,用于存储 canvas 上框的宽度和高度。 这些值似乎对TextView
的大小没有影响,因为我可以看到文本超出框。boxPosition
是一个Point
对象,存储x
和y
框在 canvas.[=34 上的位置=]R.dimen.label_date
是dimens.xml
文件中的值。
我创建了多个 dimens.xml
文件(values/dimens.xml
、values-hdpi/dimens.xml
、values-xhdpi/dimens.xml
等)
我认为我可以测试大多数屏幕尺寸并计算出每个屏幕类别的文本尺寸。然而它并没有像我预期的那样工作。
这些值适用于 Samsung Galaxy S8:
Screen Width: 1440, Screen Height: 2768
Screen Density: 4.0
Screen Density DPI: 640
Screen Scaled Density: 4.0
这些值适用于 Samsung Galaxy S7:
Screen Width: 1440, Screen Height: 2560
Screen Density: 4.0
Screen Density DPI: 640
Screen Scaled Density: 4.0
它们都属于 XXXHDPI
类别,因此它们具有相同的字体大小,但 Galaxy S8 文本适合方框,而 Galaxy S7 文本太大而超出方框。
我是不是漏掉了什么?
有什么完全不同的方法可以让我在 Canvas 上绘制 HTML 文字吗?
============================================= =====
编辑:
更详细地解释我的用例:
我指的盒子是一个 SVG/Vector 对象。
label_down.xml
:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="45dp"
android:viewportWidth="90"
android:viewportHeight="45">
<path
android:fillColor="?attr/labelColor"
android:pathData="M 0 0 L 0 37.532 L 36.63 37.532 L 45 45 L 53.371 37.532 L 90 37.532 L 90 0 Z" />
</vector>
然后我从该向量创建一个 Bitamp
对象并在 Canvas 上绘制 Bitmap
。
VectorDrawableCompat vectorDrawableCompat = VectorDrawableCompat.create(getContext().getResources(), R.drawable.label_down, theme);
Bitmap bitmap = Bitmap.createBitmap(vectorDrawableCompat.getIntrinsicWidth(), vectorDrawableCompat.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
this.canvas.drawBitmap(bitmap, someXPos, someYPos, null);
这样做会导致这样的结果:
现在,我的目标是在该框内创建文本。
希望这更有意义。
HTML 文本无法正确呈现,我建议将 DOM 对象绘制成 canvas
我认为这指出了 "sp" 和 "dp" 单位之间的区别。您没有在问题中记录尺寸文件中 label_date
的值。我假设,因为它是文本大小的单位,所以您使用的是 "sp" 单位。
如果是,请将 "xxsp" 更改为 "xxdp" 以检查您是否发现差异。我刚刚在 Nexus 5X API 26 模拟器和 Galaxy S7 运行 Android 7.0 上试过这个,"dp" 单元解决了你的 HTML 文本的问题。这是我看到的:
Nexus 5X API 26 模拟器
Galaxy S7 设备 API Android 7.0
更新: 我很好奇到底发生了什么。看起来可缩放像素 (SP) 的缩放因子大于 S7 设置中的一个。转到 "Settings->Display->Screen zoom and font" 并将 "FONT SIZE" 设置为第三个井号。您的文字现在应该可以正确显示了。
您不希望文本独立于图形进行缩放,因此,IMO,使用密度无关像素 (DP) 作为文本单位是保持图形和图形之间一致性的正确做法文字.
第二次更新:对于给定的屏幕size/density,只要你的图形大小是固定的,字体显示的大小可以根据设备设置,您将继续有文本不适合的可能性。如果您不认为将与密度无关的像素指定为字体的单位,您可以继续使用可缩放像素,但调整代码中使用的字体大小。像这样:
float fontScale = getResources().getConfiguration().fontScale;
textview.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources()
.getDimensionPixelSize(R.dimen.label_date_sp) / fontScale);
这会将字体的有效缩放系数调整回 1.0。这里的缺点是,虽然您可能指定 14sp
字体大小,但实际显示的大小会有所不同,并且会小于其他同样为 14sp
的元素。这与指定 "DP" 具有相同的效果,但将使用更标准的 "SP" 单位。