Are sp sizes in Jetpack Compose different from XML sp sizes?

Viewed 314

This might be an odd question, but is sp as defined in Jetpack Compose different from sp in XML? We have an XML style that we use in TextView defined as follows:

    <style name="Body">
        <item name="android:textSize">12sp</item>
        <item name="android:lineSpacingExtra">8dp</item>
        <item name="android:letterSpacing">-0.03125</item>
    </style>

We also have a font family defined in XML and applied to the theme via android:fontFamily and fontFamily:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">

    <font
        app:font="@font/some_font_regular"
        app:fontStyle="normal"
        app:fontWeight="500" />

    <font
        app:font="@font/some_font_italic"
        app:fontStyle="italic"
        app:fontWeight="500" />

    <font
        app:font="@font/some_font_bold"
        app:fontStyle="normal"
        app:fontWeight="700" />

    <font
        app:font="@font/cera_pro_bold_italic"
        app:fontStyle="italic"
        app:fontWeight="700" />

</font-family>

Moving to Jetpack Compose, we have a data class that contains TextStyles and the definition of the font family where we tried to mimic the definition in the XML:

data class AppTypography(
    private val someFontFamily: FontFamily = FontFamily(
        Font(R.font.some_font_regular, FontWeight.W500, FontStyle.Normal),
        Font(R.font.some_font_bold, FontWeight.W700, FontStyle.Normal),
        Font(R.font.some_font_italic, FontWeight.W500, FontStyle.Italic),
        Font(R.font.some_font_pro_bold_italic, FontWeight.W700, FontStyle.Italic),
    ),
    // ...
    val body: TextStyle = TextStyle(
        fontFamily = someFontFamily,
        fontWeight = FontWeight.Normal,
        fontSize = 12.sp,
        lineHeight = 24.sp
    ),
    // ...
)
internal val LocalTypography = staticCompositionLocalOf { AppTypography() }

This data class is in turn used in this object, which provides access to typography, colors, etc:

object AppTheme {
    val colors: AppColors
        @Composable
        get() = LocalColors.current
    val typography: AppTypography
        @Composable
        get() = LocalTypography.current
    val dimensions: AppDimensions
        @Composable
        get() = LocalDimensions.current
    val shapes: AppShapes
        @Composable
        get() = LocalShapes.current
}

Finally, we use this object as an actual Composable:

@Composable
fun AppTheme(
    colors: AppColors = lightColors(),
    content: @Composable () -> Unit
) {
    val rememberedColors = remember { colors.copy() }.apply { updateColorsFrom(colors) }
    CompositionLocalProvider(
        LocalColors provides rememberedColors,
        LocalTypography provides AppTheme.typography,
        LocalDimensions provides AppTheme.dimensions,
        LocalShapes provides AppTheme.shapes,
    ) {
        content()
    }
}

We then use the body style in one of our Texts:

Text(
    modifier = Modifier.padding(start = 16.dp),
    text = stringResource(stringResourceId),
    style = AppTheme.typography.body,
    color = AppTheme.colors.onSurface
)

Comparing the text size in the traditional View system versus in Compose (XML on the left, Compose on the right):

enter image description here

Both use 12sp as text size, but the Compose one looks smaller. To be fair even the icon looks smaller as well. I'm not sure what could cause this difference, maybe the outer Composable's paddings may have an effect? Anyone faced a similar behaviour when migrating their views to Compose?

1 Answers

So the advice I got after filing an issue with Google about this is to tinker with attributes such as letterSpacing and includeFontPadding (available in Compose 1.2.0 I believe). In my case, using the Body style I mentioned, I got close to how fonts look like in XML by:

  • setting letterSpacing to (-0.5).sp
  • setting fontSize to 16.sp
  • setting lineHeight to 24.sp

I also got confused with how font sizes were defined in our app--we have two dimens.xml files, one for hdpi and xxhdpi, and it turns out I used the smaller hdpi font size (12 sp) when I translated the font sizes from XML to Compose when I should've used the larger one (16 sp). I haven't tried a solution to having different font sizes depending on density, but this approach looks promising.

Related