Created
April 6, 2023 07:24
-
-
Save mxalbert1996/0f0fdebd640a4f22a0125a88f4cf2eab to your computer and use it in GitHub Desktop.
Auto Size (Auto Shrink) Text
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import androidx.compose.foundation.text.selection.SelectionContainer | |
import androidx.compose.material.LocalContentAlpha | |
import androidx.compose.material.LocalContentColor | |
import androidx.compose.material.LocalTextStyle | |
import androidx.compose.material.Text | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.remember | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.draw.drawBehind | |
import androidx.compose.ui.graphics.Color | |
import androidx.compose.ui.graphics.graphicsLayer | |
import androidx.compose.ui.graphics.takeOrElse | |
import androidx.compose.ui.layout.Layout | |
import androidx.compose.ui.node.Ref | |
import androidx.compose.ui.semantics.getTextLayoutResult | |
import androidx.compose.ui.semantics.semantics | |
import androidx.compose.ui.semantics.text | |
import androidx.compose.ui.text.AnnotatedString | |
import androidx.compose.ui.text.ExperimentalTextApi | |
import androidx.compose.ui.text.Paragraph | |
import androidx.compose.ui.text.TextLayoutResult | |
import androidx.compose.ui.text.TextStyle | |
import androidx.compose.ui.text.drawText | |
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.TextUnit | |
import androidx.compose.ui.unit.constrain | |
import androidx.compose.ui.unit.isSpecified | |
/** | |
* A component similar to [Text] but can automatically shrink the text to fit the constraints | |
* if the text is too big. | |
* | |
* Note that this component doesn't support text selection through [SelectionContainer]. | |
* | |
* @param text The text to be displayed. | |
* @param modifier [Modifier] to apply to this layout node. | |
* @param color [Color] to apply to the text. If [Color.Unspecified], and [style] has no color set, | |
* this will be [LocalContentColor]. | |
* @param fontSize The size of glyphs to use when painting the text. See [TextStyle.fontSize]. | |
* @param minFontSize The minimum font size to shrink the text to. [TextUnit.Unspecified] means | |
* no minimum font size. | |
* @param autoShrinkMultiplier The number to multiply the font size by when shrinking the text. | |
* Must be between 0 and 1 (exclusive). | |
* @param fontStyle The typeface variant to use when drawing the letters (e.g., italic). | |
* See [TextStyle.fontStyle]. | |
* @param fontWeight The typeface thickness to use when painting the text (e.g., [FontWeight.Bold]). | |
* @param fontFamily The font family to be used when rendering the text. See [TextStyle.fontFamily]. | |
* @param letterSpacing The amount of space to add between each letter. | |
* See [TextStyle.letterSpacing]. | |
* @param textDecoration The decorations to paint on the text (e.g., an underline). | |
* See [TextStyle.textDecoration]. | |
* @param textAlign The alignment of the text within the lines of the paragraph. | |
* See [TextStyle.textAlign]. | |
* @param lineHeight Line height for the [Paragraph] in [TextUnit] unit, e.g. SP or EM. | |
* See [TextStyle.lineHeight]. | |
* @param overflow How visual overflow should be handled. | |
* @param softWrap Whether the text should break at soft line breaks. If false, the glyphs in the | |
* text will be positioned as if there was unlimited horizontal space. If [softWrap] is false, | |
* [overflow] and TextAlign may have unexpected effects. | |
* @param maxLines An optional maximum number of lines for the text to span, wrapping if | |
* necessary. If the text exceeds the given number of lines, it will be truncated according to | |
* [overflow] and [softWrap]. If it is not null, then it must be greater than zero. | |
* @param onTextLayout Callback that is executed when a new text layout is calculated. A | |
* [TextLayoutResult] object that callback provides contains paragraph information, size of the | |
* text, baselines and other details. The callback can be used to add additional decoration or | |
* functionality to the text. For example, to draw selection around the text. | |
* @param style Style configuration for the text such as color, font, line height etc. | |
*/ | |
@OptIn(ExperimentalTextApi::class) | |
@Composable | |
fun AutoShrinkText( | |
text: AnnotatedString, | |
modifier: Modifier = Modifier, | |
color: Color = Color.Unspecified, | |
fontSize: TextUnit = TextUnit.Unspecified, | |
minFontSize: TextUnit = TextUnit.Unspecified, | |
autoShrinkMultiplier: Float = 0.9f, | |
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 | |
) { | |
val textColor = color.takeOrElse { | |
style.color.takeOrElse { | |
LocalContentColor.current.copy(alpha = LocalContentAlpha.current) | |
} | |
} | |
val mergedStyle = style.merge( | |
TextStyle( | |
color = textColor, | |
fontSize = fontSize, | |
fontWeight = fontWeight, | |
textAlign = textAlign, | |
lineHeight = lineHeight, | |
fontFamily = fontFamily, | |
textDecoration = textDecoration, | |
fontStyle = fontStyle, | |
letterSpacing = letterSpacing | |
) | |
) | |
val textMeasurer = rememberTextMeasurer() | |
val textLayoutResult = remember { Ref<TextLayoutResult>() } | |
Layout( | |
modifier = modifier | |
.graphicsLayer() | |
.drawBehind { | |
textLayoutResult.value?.let { | |
drawText(it) | |
} | |
} | |
.semantics { | |
this.text = text | |
getTextLayoutResult { textLayoutResults -> | |
textLayoutResult.value?.let { | |
textLayoutResults.add(it) | |
true | |
} ?: false | |
} | |
} | |
) { _, constraints -> | |
var result: TextLayoutResult | |
var actualStyle = mergedStyle | |
while (true) { | |
result = textMeasurer.measure( | |
text, actualStyle, overflow, softWrap, maxLines, emptyList(), constraints | |
) | |
if (result.hasVisualOverflow) { | |
var nextFontSize = actualStyle.fontSize * autoShrinkMultiplier | |
if (minFontSize.isSpecified) { | |
if (actualStyle.fontSize == minFontSize) break | |
if (nextFontSize < minFontSize) { | |
nextFontSize = minFontSize | |
} | |
} | |
actualStyle = actualStyle.copy(fontSize = nextFontSize) | |
} else { | |
break | |
} | |
} | |
onTextLayout(result) | |
textLayoutResult.value = result | |
val (width, height) = constraints.constrain(result.size) | |
layout(width, height) {} | |
} | |
} | |
/** | |
* A component similar to [Text] but can automatically shrink the text to fit the constraints | |
* if the text is too big. | |
* | |
* Note that this component doesn't support text selection through [SelectionContainer]. | |
* | |
* @param text The text to be displayed. | |
* @param modifier [Modifier] to apply to this layout node. | |
* @param color [Color] to apply to the text. If [Color.Unspecified], and [style] has no color set, | |
* this will be [LocalContentColor]. | |
* @param fontSize The size of glyphs to use when painting the text. See [TextStyle.fontSize]. | |
* @param minFontSize The minimum font size to shrink the text to. | |
* @param autoSizeMultiplier The number to multiply the font size by when shrinking the text. | |
* Must be between 0 and 1 (exclusive). | |
* @param fontStyle The typeface variant to use when drawing the letters (e.g., italic). | |
* See [TextStyle.fontStyle]. | |
* @param fontWeight The typeface thickness to use when painting the text (e.g., [FontWeight.Bold]). | |
* @param fontFamily The font family to be used when rendering the text. See [TextStyle.fontFamily]. | |
* @param letterSpacing The amount of space to add between each letter. | |
* See [TextStyle.letterSpacing]. | |
* @param textDecoration The decorations to paint on the text (e.g., an underline). | |
* See [TextStyle.textDecoration]. | |
* @param textAlign The alignment of the text within the lines of the paragraph. | |
* See [TextStyle.textAlign]. | |
* @param lineHeight Line height for the [Paragraph] in [TextUnit] unit, e.g. SP or EM. | |
* See [TextStyle.lineHeight]. | |
* @param overflow How visual overflow should be handled. | |
* @param softWrap Whether the text should break at soft line breaks. If false, the glyphs in the | |
* text will be positioned as if there was unlimited horizontal space. If [softWrap] is false, | |
* [overflow] and TextAlign may have unexpected effects. | |
* @param maxLines An optional maximum number of lines for the text to span, wrapping if | |
* necessary. If the text exceeds the given number of lines, it will be truncated according to | |
* [overflow] and [softWrap]. If it is not null, then it must be greater than zero. | |
* @param onTextLayout Callback that is executed when a new text layout is calculated. A | |
* [TextLayoutResult] object that callback provides contains paragraph information, size of the | |
* text, baselines and other details. The callback can be used to add additional decoration or | |
* functionality to the text. For example, to draw selection around the text. | |
* @param style Style configuration for the text such as color, font, line height etc. | |
*/ | |
@Composable | |
fun AutoShrinkText( | |
text: String, | |
modifier: Modifier = Modifier, | |
color: Color = Color.Unspecified, | |
fontSize: TextUnit = TextUnit.Unspecified, | |
minFontSize: TextUnit = TextUnit.Unspecified, | |
autoSizeMultiplier: Float = 0.9f, | |
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 | |
) { | |
AutoShrinkText( | |
AnnotatedString(text), | |
modifier, | |
color, | |
fontSize, | |
minFontSize, | |
autoSizeMultiplier, | |
fontStyle, | |
fontWeight, | |
fontFamily, | |
letterSpacing, | |
textDecoration, | |
textAlign, | |
lineHeight, | |
overflow, | |
softWrap, | |
maxLines, | |
onTextLayout, | |
style | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment