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

如何在StaticLayout中通过编程实现文本两端对齐?

如何在StaticLayout中实现文本两端对齐

原生StaticLayout的setAlignment()仅提供ALIGN_CENTER、ALIGN_NORMAL、ALIGN_OPPOSITE三种对齐方式,没有直接的两端对齐选项。要实现该效果,需要通过自定义绘制逻辑手动调整每行文本的间距,以下是具体实现方案:

核心思路

  1. 先通过StaticLayout计算文本的换行、每行范围和实际宽度
  2. 对除最后一行外的所有行,根据该行文本实际宽度与布局宽度的差值,分配额外间距:
    • 若文本包含空格:将额外间距平均分配给所有空格
    • 若文本无空格:拉伸字符间的间距

代码示例(Kotlin)

1. 创建StaticLayout实例

val targetText = "这是一段需要实现两端对齐的测试文本,用来验证多行情况下的对齐效果。如果文本中没有空格,就需要拉伸字符间距来实现两端对齐。"
val textPaint = TextPaint().apply {
    textSize = spToPx(16f) // 转换sp为px
    color = Color.BLACK
}
val layoutWidth = 500 // 布局的总宽度
val staticLayout = StaticLayout.Builder.obtain(
    targetText,
    0,
    targetText.length,
    textPaint,
    layoutWidth
)
    .setAlignment(Layout.Alignment.ALIGN_NORMAL)
    .build()

// 工具方法:sp转px
private fun spToPx(sp: Float): Float {
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, resources.displayMetrics)
}

2. 自定义View实现两端对齐绘制

class JustifiedTextView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
    private val text = "这是一段需要实现两端对齐的测试文本,用来验证多行情况下的对齐效果。如果文本中没有空格,就需要拉伸字符间距来实现两端对齐。"
    private val textPaint = TextPaint().apply {
        textSize = spToPx(16f)
        color = Color.BLACK
    }
    private lateinit var staticLayout: StaticLayout
    private val layoutWidth = 500

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        // 初始化StaticLayout
        staticLayout = StaticLayout.Builder.obtain(text, 0, text.length, textPaint, layoutWidth)
            .setAlignment(Layout.Alignment.ALIGN_NORMAL)
            .build()
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        var currentY = 0f
        val totalLines = staticLayout.lineCount

        for (lineIndex in 0 until totalLines) {
            val lineStart = staticLayout.getLineStart(lineIndex)
            val lineEnd = staticLayout.getLineEnd(lineIndex)
            val lineContent = text.substring(lineStart, lineEnd)
            val lineActualWidth = staticLayout.getLineWidth(lineIndex)

            // 最后一行或单行文本不做两端对齐处理
            if (lineIndex == totalLines - 1 && lineActualWidth <= layoutWidth) {
                canvas.drawText(lineContent, 0f, currentY + staticLayout.getLineTop(lineIndex), textPaint)
                currentY += staticLayout.getLineHeight(lineIndex)
                continue
            }

            // 计算需要补充的总间距
            val extraSpace = layoutWidth - lineActualWidth
            if (extraSpace <= 0) {
                // 文本宽度已占满,直接绘制
                canvas.drawText(lineContent, 0f, currentY + staticLayout.getLineTop(lineIndex), textPaint)
                currentY += staticLayout.getLineHeight(lineIndex)
                continue
            }

            // 处理带空格的文本:给空格分配额外间距
            val spaceCount = lineContent.count { it == ' ' }
            if (spaceCount > 0) {
                val extraSpacePerSpace = extraSpace / spaceCount
                var currentX = 0f
                for (char in lineContent) {
                    canvas.drawText(char.toString(), currentX, currentY + staticLayout.getLineTop(lineIndex), textPaint)
                    // 普通字符加自身宽度,空格加自身宽度+额外间距
                    currentX += textPaint.measureText(char.toString()) + if (char == ' ') extraSpacePerSpace else 0f
                }
            } else {
                // 无空格文本:拉伸字符间距
                val letterCount = lineContent.length
                if (letterCount <= 1) {
                    canvas.drawText(lineContent, 0f, currentY + staticLayout.getLineTop(lineIndex), textPaint)
                } else {
                    val extraLetterSpacing = extraSpace / (letterCount - 1)
                    val originalSpacing = textPaint.letterSpacing
                    textPaint.letterSpacing = extraLetterSpacing
                    canvas.drawText(lineContent, 0f, currentY + staticLayout.getLineTop(lineIndex), textPaint)
                    textPaint.letterSpacing = originalSpacing // 恢复原间距
                }
            }
            currentY += staticLayout.getLineHeight(lineIndex)
        }
    }

    private fun spToPx(sp: Float): Float {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, resources.displayMetrics)
    }
}

注意事项

  • 最后一行通常不需要两端对齐,保持左对齐即可,符合常规阅读习惯
  • 无空格文本的字符拉伸效果可能会影响视觉体验,建议尽量避免这种场景
  • 若需要支持中文排版中的标点避头等更复杂的规则,还需额外处理文本细节

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 16:21:10