Search code examples
androidandroid-jetpack-composeandroid-jetpack

How to remove leftover space after applying scale modifier in Android Compose


In this app, I was going to put a scrollable list of composable with each have a size relative to the device screen but then scale down to a few pixel. And I found the best way to keep the layout unchanged and consistent to when it's fullscreen is to make the size of the composable exactly the same as the device screen and then resize it with scale modifier

        Row(
            modifier = Modifier.horizontalScroll(rememberScrollState())
        ) {
            Surface(
                shape = RoundedCornerShape(8.dp),
                color = MaterialTheme.colors.primary.copy(alpha = .2f),
            ) {
                ScreenVisual(
                    modifier = Modifier
                        .size(screenWidth, screenHeight)
                        .scale(.7F)
                )
            }
            Surface(
                shape = RoundedCornerShape(8.dp),
                color = MaterialTheme.colors.primary.copy(alpha = .2f),
            ) {
                OtherScreenVisual(
                    modifier = Modifier
                        .size(screenWidth, screenHeight)
                        .scale(.7F)
                )
            }
        }

enter image description here

But then as shown on the image above, there are leftover spaces from the composable after the scale modifier, the question is how to remove those spaces and force each surface to stick with each other


Solution

  • You can achieve it by combining size, requiredSize and scale modifiers.

    Content(
        modifier = Modifier
            .size(width = screenWidth * scale, height = screenHeight * scale)
            .requiredSize(width = screenWidth, height = screenHeight)
            .scale(scale)
    )
    
    • size(screenWidth * scale, screenHeight * scale) means that you would reserve just the amount of space that's equals to scaled screen size.
    • requiredSize(screenWidth, screenHeight) forces to measure and layout content with real screen size, so it will be rendered with normal scale and centred inside that previously defined frame. Of course at that stage the rendered content would be too big for that frame.
    • scale(scale) would scale the content inside to fit the frame declared with .size

    You can see that entire content is scaled properly. In this case Texts with 30.sp are scaled down compared to the Text on top (that is not scaled in any way).

    Entire sample looks like this:

    Column {
        Text("Text 30sp (for comparison)", fontSize = 30.sp, modifier = Modifier.weight(1f))
    
        Row(Modifier.horizontalScroll(state = rememberScrollState())) {
    
            Content1(
                modifier = Modifier
                    .size(width = screenWidth * scale, height = screenHeight * scale)
                    .requiredSize(width = screenWidth, height = screenHeight)
                    .scale(scale)
            )
    
            Content2(
                modifier = Modifier
                    .size(width = screenWidth * scale, height = screenHeight * scale)
                    .requiredSize(width = screenWidth, height = screenHeight)
                    .scale(scale)
            )
        }
    }
    

    And example of how the sample Content1 looks like:

    @Composable
    private fun Content1(modifier: Modifier = Modifier) {
        Text(
            "Text 30sp",
            fontSize = 30.sp,
            modifier = modifier
                .fillMaxSize()
                .background(Color.Blue)
        )
    }
    
    ...
    

    enter image description here