位置:首页 > Kotlin > Jetpack Compose中实现AutoSizeText自适应文字大小

Jetpack Compose中实现AutoSizeText自适应文字大小

时间:2026-08-21  |  作者:极客少年  |  阅读:0

使用二分查找法实现Text的字体大小自适应,代码如下:

compose 实现AutoSizeText

import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextMeasurer
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp

@Composable
fun AutoSizeText(
    text: String,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontStyle: FontStyle = null,
    fontWeight: FontWeight = null,
    fontFamily: FontFamily = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration = null,
    textAlign: TextAlign = TextAlign.Unspecified,
    lineHeight: TextUnit = TextUnit.Unspecified,
    maxLines: Int = 1,
    overflow: TextOverflow = TextOverflow.Ellipsis,
    onTextLayout: ((TextLayoutResult) -> Unit) = null,
    style: TextStyle = LocalTextStyle.current.copy(
        color = color,
        fontStyle = fontStyle,
        fontWeight = fontWeight,
        fontFamily = fontFamily,
        letterSpacing = letterSpacing,
        textDecoration = textDecoration,
        textAlign = textAlign,
        lineHeight = lineHeight,
    ),
    maxFontSize: TextUnit = 100.sp,
    minFontSize: TextUnit = 10.sp,
) {
    val textMeasurer = rememberTextMeasurer()

    BoxWithConstraints(modifier = modifier) {
        // 1. 获取容器的约束尺寸
        val maxWidthPx = constraints.maxWidth
        val maxHeightPx = constraints.maxHeight

        // 2. 使用 remember 缓存计算结果,只有 text 或约束改变时才重新计算
        val optimizedFontSize = remember(text, maxWidthPx, maxHeightPx) {
            var low = minFontSize.value
            var high = maxFontSize.value
            var res = low

            // 二分查找循环
            while (low <= high) {

                val mid = (low + high) / 2
                val isFit = checkTextFit(
                    textMeasurer = textMeasurer,
                    text = text,
                    fontSize = mid.sp,
                    style = style,
                    maxWidthPx = maxWidthPx,
                    maxHeightPx = maxHeightPx,
                    maxLines = maxLines
                )
                if (isFit) {
                    res = mid
                    low = mid + 0.5f // 尝试更大的字号(步长可调)
                } else {
                    high = mid - 0.5f // 字号太大了,调小一点
                }
            }
            res.sp
        }

        // 3. 最终渲染
        Text(
            text = text,
            color = color,
            fontSize = optimizedFontSize,
            style = style,
            maxLines = maxLines,
            overflow = overflow,
            onTextLayout = onTextLayout
        )
    }
}

/**
 * 核心辅助函数:使用 TextMeasurer 在内存中测量文字是否溢出
 */
private fun checkTextFit(
    textMeasurer: TextMeasurer,
    text: String,
    fontSize: TextUnit,
    style: TextStyle,
    maxWidthPx: Int,
    maxHeightPx: Int,
    maxLines: Int
): Boolean {
    val layoutResult = textMeasurer.measure(
        text = AnnotatedString(text),
        style = style.copy(fontSize = fontSize),
        maxLines = maxLines,
        constraints = Constraints(maxWidth = maxWidthPx, maxHeight = maxHeightPx)
    )
    // 同时检查宽度和高度是否在容器范围内
    return layoutResult.size.width < maxWidthPx && layoutResult.size.height < maxHeightPx
}

以上是完整代码,可直接使用。

简单分析

核心逻辑在于:通过checkTextFit方法判断文字是否超出容器范围。

  • 如果超出了,就执行high = mid - 0.5f,表示把字号调小一点。

  • 如果没有超出,就执行low = mid + 0.5f,表示继续尝试更大的字号。

  • 这里的步长是可以调整的。

整个过程会持续循环,直到low > high

当循环结束后,得到的res就是自适应的字体大小。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多