5

I am building an Android App which needs to create several vertical sliders in the same page for music equalizer adjustment, but I can only find horizontal sliders from the official material design documents.

I try to implement default slider from official documents and rotate it with modifier and it works, but the problem is that I am not able to adjust the height now using Modifier.

screenshot

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Row(modifier =
            Modifier
                .fillMaxWidth()
                .fillMaxHeight(),
               horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically
                ) {
                slider()
                slider()
                slider()
                slider()
                slider()
                slider()
                slider()

            }

        }
    }
}



@Composable
fun slider() : Int
{
    var sliderPosition by remember { mutableStateOf(0f) }

    Slider(
        modifier = Modifier
            .width(50.dp)
            .height(120.dp)
            .background(color = Color.Red)
            .wrapContentSize()
            .rotate(270F)
            .padding(start = 0.5.dp),

        value = sliderPosition,
        valueRange = 1f..10f,
        onValueChange = {sliderPosition = it}
    )

    return sliderPosition.roundToInt()
}

hippietrail
  • 15,848
  • 18
  • 99
  • 158
Sio Chung
  • 65
  • 1
  • 6
  • 2
    I recommend using the modifier as the last argument, so that you can omit the comma: then it will be much easier to add/remove/swap modifiers. – Phil Dukhov Feb 15 '22 at 15:54

1 Answers1

8

Your rotated slider doesn't fill available height, because it gets a wrong constraint after rotation. To fix this you first of all need to swap width and height values.

Next, Modifier.rotate won't change the view position, so you need to manually update it after rotation. To understand what's going on, you can run the following code:

Box(
    contentAlignment = Alignment.Center,
    modifier = Modifier.fillMaxSize()
) {
    Box(Modifier
        .size(100.dp, 50.dp)
        .border(width = 1.dp, color = Color.Red)
        .rotate(90f)
        .background(Color.Green))
}

It'll produce the following view:

As you can see, real view frame is shown with the red border, and your view has an unexpected offset.

To fix it, you can use Modifier.layout: you need to swap constraints and result view size dimensions, and revert the offset.

Also I'm using Modifier.graphicsLayer with transformOrigin set to zero point, so it's easier to calculate an offset needed in Modifier.layout.

And the last part, size modifier should be placed after Modifier.layout, in order to get parent view constraints instead of this static ones during layout. And layout should be placed after rotation. Check out more about why modifiers order matters in this answer

Final modifier for your slider:

Modifier
    .graphicsLayer {
        rotationZ = 270f
        transformOrigin = TransformOrigin(0f, 0f)
    }
    .layout { measurable, constraints ->
        val placeable = measurable.measure(
            Constraints(
                minWidth = constraints.minHeight,
                maxWidth = constraints.maxHeight,
                minHeight = constraints.minWidth,
                maxHeight = constraints.maxHeight,
            )
        )
        layout(placeable.height, placeable.width) {
            placeable.place(-placeable.width, 0)
        }
    }
    .width(120.dp)
    .height(50.dp)
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • Thanks Philip, this fixed my problem. But now I have another issue, I need 2 labels, one on the top and the other on the bottom of the slider. If I set a fixed width value in modifier like this `.width(180.dp) `, it will be fine. https://imgur.com/a/DmV6rYO However I would like to let the slider height responsive to the device screen size, so I set the width like this `.fillMaxWidth()` in modifier, the bottom label will disappear. https://imgur.com/a/QMRys5i Is there any ways to fix this? Thanks so much! – Sio Chung Feb 17 '22 at 09:03
  • @SioChung use `Modifier.weight(1f)` instead of `fillMaxWidth()` – Phil Dukhov Feb 17 '22 at 09:28
  • There seems no `.weight()` available in Slider compose. – Sio Chung Feb 17 '22 at 09:52
  • @SioChung it should be available inside `Row`, how else are you adding the text views? – Phil Dukhov Feb 17 '22 at 10:14
  • @SioChung I mean a column ofc. You can define your slider to be used only inside a column, with `fun ColumnScope.slider()`, or pass modifier as a parameter – Phil Dukhov Feb 17 '22 at 11:00
  • Hi Philip, I tried to set in `Row` but still not working. I try to elaborate my question [here](https://stackoverflow.com/q/71167811/16755472) to show how I add the text views. – Sio Chung Feb 18 '22 at 02:45