You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android Studio中实现TextView内部嵌套另一个TextView

Android 消息TextView右下角嵌入时间、文本自动绕排实现方案

以下是两种稳定可行的实现方式,均可以实现消息文本不覆盖时间、自动适配换行的效果:

方案一:自定义TextView内置时间绘制(性能最优,无额外布局嵌套)

  • 核心逻辑:重写TextView的测量、绘制流程,自动计算文本换行位置预留时间区域,直接在控件右下角绘制时间文本,不需要额外嵌套布局。
  • 核心实现代码:
class MessageTextView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {
    // 时间文本样式配置,可根据需求自定义
    private val timeTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, resources.displayMetrics)
        color = Color.parseColor("#999999")
    }
    private var timeStr = ""
    private val timeHorizontalPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6f, resources.displayMetrics).toInt()
    private var timeWidth = 0
    private var timeLineHeight = 0

    // 对外暴露设置时间的方法
    fun setTime(time: String) {
        timeStr = time
        timeWidth = timeTextPaint.measureText(timeStr).toInt()
        timeLineHeight = (timeTextPaint.descent() - timeTextPaint.ascent()).toInt()
        requestLayout()
        invalidate()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val textLayout = layout ?: return
        // 计算最后一行剩余宽度,不足放下时间则增加控件高度
        val lastLineAvailableWidth = measuredWidth - paddingLeft - paddingRight - textLayout.getLineWidth(lineCount - 1)
        if (lastLineAvailableWidth < timeWidth + timeHorizontalPadding) {
            setMeasuredDimension(measuredWidth, measuredHeight + timeLineHeight + timeHorizontalPadding / 2)
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (timeStr.isEmpty()) return
        // 右下角绘制时间文本
        val drawX = (measuredWidth - paddingRight - timeWidth).toFloat()
        val drawY = (measuredHeight - paddingBottom - timeTextPaint.descent())
        canvas.drawText(timeStr, drawX, drawY, timeTextPaint)
    }
}
  • 使用方式:直接在布局中替换原有的消息TextView,调用setTime方法传入时间字符串即可。

方案二:Span占位+独立时间TextView(实现简单,适配性强)

  • 核心逻辑:计算时间文本的宽度,在消息文本末尾插入对应宽度的占位Span让文本自动绕开时间区域,再通过布局将时间TextView固定在消息TextView的右下角。
  • 核心实现代码:
// 1. 测量时间文本所需宽度
val timeStr = "18:25"
val timeTextSize = 12f // sp单位
val timePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, timeTextSize, resources.displayMetrics)
}
val timeTotalWidth = timePaint.measureText(timeStr) + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics)

// 2. 给消息文本添加占位Span
val messageContent = "需要展示的消息内容,支持任意长度自动换行"
val spannableContent = SpannableStringBuilder(messageContent)
spannableContent.setSpan(
    object : ReplacementSpan() {
        override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?) = timeTotalWidth.toInt()
        override fun draw(canvas: Canvas, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {}
    },
    spannableContent.length - 1,
    spannableContent.length,
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)

// 3. 赋值给控件,布局中需将时间TextView约束在消息TextView的右下角
messageTv.text = spannableContent
timeTv.text = timeStr
  • 布局参考:使用ConstraintLayout作为父布局,给时间TextView添加layout_constraintBottom_toBottomOf="@id/messageTv"、layout_constraintEnd_toEndOf="@id/messageTv"、android:layout_marginEnd="8dp"、android:layout_marginBottom="4dp"属性即可。

内容的提问来源于stack exchange,提问作者Shaurya Goyal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 16:36:03