java - 计算基线 y 以在 Android 中的画布上绘制文本
问题描述
我正在使用以下代码来绘制文本。
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
final Rect rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
final int width = rect.width();
final int height = rect.height();
Bitmap rendered = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas gOfRendered = new Canvas(rendered);
paint.setColor(Color.WHITE);
gOfRendered.drawText(
text, 0, height - paint.getFontMetrics().descent - paint.getFontMetrics().leading, paint
);
gOfRendered.drawColor(0x11ffffff); // to see the bounds
Canvas.drawText
需要 y 坐标作为基线。上面输出中的某些字符串(例如“back”)没有下降。paint.getFontMetrics().descent
它被削减了,在这些情况下减去是没有意义的。如何确保正确计算基线?
或者,是否有一种文本绘制方法采用原点 y 坐标而不是基线?
我正在寻找一种在给出的范围内精确绘制文本的方法Paint.getTextBounds()
解决方案
可以将单行文本放入BoringLayout以获取基线的位置。(也可以使用StaticLayout,但看起来您正在处理单行,所以我们将使用BoringLayout。)
下面的代码显示了如何将文本放入布局中以提取基线。一旦知道基线(相对于顶部的零),就可以在画布上我们喜欢的位置绘制文本。
我的视图.kt
class MyView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val textToDisplay: String
private var textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
textSize = 98f
color = Color.WHITE
}
// Layout for single lines
private var boringLayout: BoringLayout
// Contains just the text.
private val wordBounds = Rect()
private val wordBoundsFillPaint = Paint().apply {
color = 0x55ffffff
style = Paint.Style.FILL
}
init {
context.obtainStyledAttributes(attrs, R.styleable.MyView, 0, 0).apply {
textToDisplay = getString(R.styleable.MyView_android_text) ?: "Nothing"
}.recycle()
val metrics = BoringLayout.isBoring(textToDisplay, textPaint)
?: throw IllegalArgumentException("\"$textToDisplay\" is not boring.")
// Get the text bounds that adhere tightly to the text.
textPaint.getTextBounds(textToDisplay, 0, textToDisplay.length, wordBounds)
// Get the layout for the text. These bounds include additional spacing used in the layout.
boringLayout =
BoringLayout.make(
textToDisplay,
textPaint,
textPaint.measureText(textToDisplay).toInt(), Layout.Alignment.ALIGN_NORMAL,
0f, 0f,
metrics,
false
)
val textBaseline = boringLayout.getLineBaseline(0)
wordBounds.top = wordBounds.top + textBaseline
wordBounds.bottom = wordBounds.bottom + textBaseline
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.withTranslation(
// Center the words within the view.
(width - boringLayout.width).toFloat() / 2,
(height - boringLayout.height).toFloat() / 2
) {
drawRect(wordBounds, wordBoundsFillPaint)
// Using BoringLayout to draw text is preferred, but drawText() will work here as well.
boringLayout.draw(this)
// drawText(textToDisplay, 0f, boringLayout.getLineBaseline(0).toFloat(), textPaint)
}
}
}
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.textbaseline.MyView
android:id="@+id/myView"
android:layout_width="200dp"
android:layout_height="75dp"
android:background="@android:color/black"
android:text="Retrograde"
app:layout_constraintBottom_toTopOf="@+id/myView2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<com.example.textbaseline.MyView
android:id="@+id/myView2"
android:layout_width="200dp"
android:layout_height="75dp"
android:layout_marginTop="8dp"
android:background="@android:color/black"
android:text="Ăpple"
app:layout_constraintBottom_toTopOf="@+id/myView3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/myView" />
<com.example.textbaseline.MyView
android:id="@+id/myView3"
android:layout_width="200dp"
android:layout_height="75dp"
android:layout_marginTop="8dp"
android:background="@android:color/black"
android:text="back"
app:layout_constraintBottom_toTopOf="@+id/myView4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/myView2" />
<com.example.textbaseline.MyView
android:id="@+id/myView4"
android:layout_width="200dp"
android:layout_height="75dp"
android:layout_marginTop="8dp"
android:background="@android:color/black"
android:text="scene"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/myView3" />
</androidx.constraintlayout.widget.ConstraintLayout>
推荐阅读
- batch-file - 如何记录任务列表的计数
- vertica - 如何使重复的vertica中现有的表列值唯一?
- android - 我的 videoview 说“无法播放视频”,但 webview 可以播放吗?
- asp.net-mvc - 等待的呼叫在单元测试中丢失上下文
- javascript - 用户交互超级事件
- javascript - js如何破坏函数的默认值?
- python - 制作成对的文件和列以在循环中使用?
- selenium - 无法通过 selenium 获取 ul 标签下的所有 li 项
- arrays - 解析数组时如何简化 if..else 语句
- glsl - 如何使用6个sampler2D实现textureCube