Skip to content

Instantly share code, notes, and snippets.

@dovahkiin98
Created October 4, 2021 09:03
Show Gist options
  • Save dovahkiin98/cd4e1f639cc4f6392018e327d0db44d8 to your computer and use it in GitHub Desktop.
Save dovahkiin98/cd4e1f639cc4f6392018e327d0db44d8 to your computer and use it in GitHub Desktop.
AutoSize text implementation in Jetpack Compose
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.BoxWithConstraintsScope
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.InternalFoundationTextApi
import androidx.compose.foundation.text.TextDelegate
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFontLoader
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLayoutResult
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.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.min
import androidx.compose.ui.unit.sp
@Composable
fun AutoSizeText(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
suggestedFontSizes: List<TextUnit> = emptyList(),
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current,
) {
AutoSizeText(
AnnotatedString(text),
modifier,
color,
suggestedFontSizes,
fontStyle,
fontWeight,
fontFamily,
letterSpacing,
textDecoration,
textAlign,
lineHeight,
overflow,
softWrap,
maxLines,
emptyMap(),
onTextLayout,
style,
)
}
@Composable
fun AutoSizeText(
text: AnnotatedString,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
suggestedFontSizes: List<TextUnit> = emptyList(),
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
inlineContent: Map<String, InlineTextContent> = mapOf(),
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current,
) {
BoxWithConstraints(
modifier = modifier,
contentAlignment = Alignment.Center,
) {
var combinedTextStyle = (LocalTextStyle.current + style).copy(
fontSize = min(maxWidth, maxHeight).value.sp
)
val fontSizes = suggestedFontSizes.ifEmpty {
MutableList(combinedTextStyle.fontSize.value.toInt()) {
(combinedTextStyle.fontSize.value - it).sp
}
}
var currentFontIndex = 0
while (shouldShrink(text, combinedTextStyle, maxLines)) {
combinedTextStyle =
combinedTextStyle.copy(fontSize = fontSizes[++currentFontIndex])
}
Text(
text,
Modifier,
color,
TextUnit.Unspecified,
fontStyle,
fontWeight,
fontFamily,
letterSpacing,
textDecoration,
textAlign,
lineHeight,
overflow,
softWrap,
maxLines,
inlineContent,
onTextLayout,
combinedTextStyle,
)
}
}
@OptIn(InternalFoundationTextApi::class)
@Composable
private fun BoxWithConstraintsScope.shouldShrink(
text: AnnotatedString,
textStyle: TextStyle,
maxLines: Int
): Boolean {
val textDelegate = TextDelegate(
text,
textStyle,
maxLines,
true,
TextOverflow.Clip,
LocalDensity.current,
LocalFontLoader.current,
)
val textLayoutResult = textDelegate.layout(
constraints,
LocalLayoutDirection.current,
)
return textLayoutResult.hasVisualOverflow
}
@LeonardMaetzner
Copy link

LeonardMaetzner commented May 8, 2024

The TextDelegate of androidx.compose.foundation is marked as internal as of version 1.7.0-alpha05. Therefore, this solution can't be used without an unsightly @file:Suppress hack

@Moozart
Copy link

Moozart commented Jul 9, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment