-
-
Save CanYumusak/34e6620f444d5ba0c8f7419362d5d394 to your computer and use it in GitHub Desktop.
public class LabelLayoutModifier( | |
val context: Context, | |
val lineHeight: TextUnit, | |
val style: MyTextStyle, | |
) : LayoutModifier { | |
override fun MeasureScope.measure( | |
measurable: Measurable, | |
constraints: Constraints | |
): MeasureResult { | |
val placeable = measurable.measure(constraints) | |
val lineCount = lineCount(placeable) | |
val fullHeight = (lineHeight.toPx() * lineCount).roundToInt() | |
val fontMetrics = fontMetrics(context, style) | |
val centerOffset = floor((lineHeight.toPx().toDp() - fontMetrics.descent.toDp() + fontMetrics.ascent.toDp()).value / 2f).dp.toPx().toInt() | |
val figmaOffset = fontMetrics.ascent - fontMetrics.top | |
return layout(width = placeable.width, height = fullHeight) { | |
// Alignment lines are recorded with the parents automatically. | |
placeable.placeRelative( | |
x = 0, | |
y = (centerOffset - figmaOffset).toInt() | |
) | |
} | |
} | |
override fun IntrinsicMeasureScope.maxIntrinsicHeight( | |
measurable: IntrinsicMeasurable, | |
width: Int | |
): Int { | |
return ceilToLineHeight(measurable.maxIntrinsicHeight(width)) | |
} | |
override fun IntrinsicMeasureScope.minIntrinsicHeight( | |
measurable: IntrinsicMeasurable, | |
width: Int | |
): Int { | |
return ceilToLineHeight(measurable.minIntrinsicHeight(width)) | |
} | |
override fun IntrinsicMeasureScope.minIntrinsicWidth( | |
measurable: IntrinsicMeasurable, | |
height: Int | |
): Int { | |
return measurable.minIntrinsicWidth(height) | |
} | |
override fun IntrinsicMeasureScope.maxIntrinsicWidth( | |
measurable: IntrinsicMeasurable, | |
height: Int | |
): Int { | |
return measurable.maxIntrinsicWidth(height) | |
} | |
private fun Density.lineCount(placeable: Placeable): Int { | |
val firstToLast = (placeable[LastBaseline] - placeable[FirstBaseline]).toFloat() | |
return (firstToLast / lineHeight.toPx()).roundToInt() + 1 | |
} | |
private fun Density.ceilToLineHeight(value: Int): Int { | |
val lineHeightPx = lineHeight.toPx() | |
return (ceil(value.toFloat() / lineHeightPx) * lineHeightPx).roundToInt() | |
} | |
} | |
private fun Density.fontMetrics(context: Context, textStyle: MyTextStyle): Paint.FontMetrics { | |
val fontResourceId = textStyle.fonts[textStyle.fontWeight]!! | |
val font = ResourcesCompat.getFont(context, fontResourceId) | |
val paint = Paint().also { | |
it.typeface = font | |
it.textSize = textStyle.fontSize.toPx() | |
} | |
return paint.fontMetrics | |
} |
public data class MyTextStyle internal constructor( | |
val fontSize: TextUnit, | |
val fontWeight: FontWeight, | |
val letterSpacing: TextUnit, | |
val lineHeight: TextUnit, | |
) { | |
public val fonts: Map<FontWeight, Int> = mapOf( | |
WaveFontWeight.Demi.fontWeight to R.font.nationale_demi_bold, | |
WaveFontWeight.Bold.fontWeight to R.font.nationale_bold, | |
) | |
private val fontFamily: FontFamily = FontFamily( | |
fonts.map { Font(it.value, it.key) } | |
) | |
internal fun asTextStyle(): TextStyle { | |
return TextStyle( | |
fontSize = fontSize, | |
fontWeight = fontWeight, | |
fontFamily = fontFamily, | |
letterSpacing = letterSpacing, | |
lineHeight = lineHeight, | |
) | |
} | |
} |
Thanks. You mean, Reducing should be just as possible as increasing it?
Sorry for not getting back to you: Yes, that's aboslutely what i meant.
@CanYumusak Hello, is this available in android view system as well? since i have the same issue, i think i manually have to calculate lineHeight like you did here but for view system.
A lot has changed in recent compose versions, thus this is now available for free without custom layouts. I didn't dig as deep on the view system but parts may be applicable, although I assume it should be tedious
so in compose if i just specify platformStyle = PlatformTextStyle(
includeFontPadding = false,
),
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
), it will match exactly like in figma? without any other calculations?
That's correct! Just make sure to set the line height, if you don't set it it will not be correct
i have also searched up and have seen that such small font mismatches can be due to the Renderer of figma and Android and this is normal, i do not know though if i am being wrong :)
Id recommend reading my article about the topic for details
https://dev.to/canyudev/android-and-figma-typography-and-how-to-achieve-100-fidelity-l40
However you are right, given that these are 2 different renderings it is possible that the result is not sub-pixel similar. I assume that this is good enough for most practical reasons though
yeah, i have already read that article, really interesting and challenging actually. thank you for your answers!
Reducing should be just as possible as reducing it. I would highly recommend to not use lineheight equal to the font size though, this is a common design mistake that does not take into account ascending and descending glyphs in the font.
You can read up on the space that fonts take up here: https://dev.to/canyudev/android-and-figma-typography-and-how-to-achieve-100-fidelity-l40
(ignore the calculations, these are pre-Compose-change)