Single-line text can be placed into a BoringLayout to get the location of the baseline. (StaticLayout can also be used, but it looks like you are dealing with single lines, so we will use BoringLayout.)
The code below shows how to place text into a layout to extract the baseline. Once the baseline is known (relative to zero at the top), the text can be drawn where we like on a canvas.
MyView.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>