0

Three sliders are defined in Jetpack Compose and the thumb positions of these three sliders are needed to connect them using a Path in a Canvas behind them. How to get the position PointF(x,y) of the thumb in each Slider to draw the Path or any other way to achieve this? Below is sample code snippet and image of sample implementation to achieve.

@Composable
fun MySliderDemo() {
    var sliderPosition1 = remember { mutableStateOf(0f) }
    var sliderPosition2 = remember { mutableStateOf(0f) }
    var sliderPosition3 = remember { mutableStateOf(0f) }

    Slider(value = sliderPosition1.value, onValueChange = { sliderPosition1.value = it })
    Slider(value = sliderPosition2.value, onValueChange = { sliderPosition2.value = it })
    Slider(value = sliderPosition3.value, onValueChange = { sliderPosition3.value = it })
}

Sample implementation

Bullionist
  • 2,070
  • 3
  • 25
  • 42

2 Answers2

0

var pos1 by remember { mutableStateOf(0f) } var pos2 by remember { mutableStateOf(0f) } var pos3 by remember { mutableStateOf(0f) }

var layoutCoordinates = 0

Slider(value = pos1, onValueChangeFinished = { newPos1 ->
    // you now have 3 slider positions:
    // newPos1, pos2, and pos3
}, onValueChange = { pos1 = it },
    modifier = Modifier.onGloballyPositioned {
        layoutCoordinates = it
    })

Slider(value = pos2, onValueChangeFinished = { newPos2 ->
    // you now have 3 slider positions:
    // pos1, newPos2, and pos3
}, onValueChange = { pos2 = it })

Slider(value = pos3, onValueChangeFinished = { newPos3 ->
    // you now have 3 slider positions:
    // pos1, pos2, and newPos3
}, onValueChange = { pos3 = it })

with every Slider's onValueChangeFinished, you get each of their values. You can also get the LayoutCoordinates of the Slider (I assume you only need to get it from one of them since they're all the same size, but if not, you can just keep track of all 3) from Modifier.onGloballyPositioned. From there, you should be able to calculate the position of each slider based on the Slider value and the layout coordinates (which give you position, size, etc.)

user496854
  • 6,461
  • 10
  • 47
  • 84
  • Thanks for your suggestion. The implementation that I would need to achieve is the other way around. I want to change the path as per the users adjustments of the slider. So I want to know the position of the Thumb in the slider. – Bullionist Aug 18 '22 at 02:16
  • That's exactly what my code does -- when the user moves the slider, onValueChange gets triggered, and adjusts the other 2 sliders – user496854 Aug 18 '22 at 02:20
  • Sorry, I think my question is not clear. I do not want the user interaction to change the position of the other sliders. I want to get the Thumb position of each slider in order to draw a path using canvas. – Bullionist Aug 18 '22 at 02:21
  • Ok, that's a relatively simple thing to do once you keep track of the 3 slider positions. Updating my answer... – user496854 Aug 18 '22 at 02:26
  • I have edited sample code to add more clarity. – Bullionist Aug 18 '22 at 02:28
  • I need the positions PointF(x,y) of the Thumbs of all three sliders in order to plot a path. – Bullionist Aug 18 '22 at 03:06
  • As I said in my answer, you can set Modifier.onGloballyPositioned() for all 3 sliders, and keep track of all 3 of them. I just thought that since you know their dimensions, and their values, you could calculate the positions of the other 2 based on the 1st one – user496854 Aug 18 '22 at 03:13
  • Sorry, the layout co-ordinates do not return the relative position of the Thumb with respect to the display. I require the PointF(x,y) positions of the each Thumb in order to draw a Path using canvas. – Bullionist Aug 18 '22 at 03:19
  • No, but you know the width in px of the slider, and you know the valueRange (presumably, you set it yourself). So, thumbRelativePosX = sliderValue / sliderMaxValue * sliderWidthPx gets you the Thumb's X coordinate relative to the Slider layout. You also have the slider's coordinates (in pixels), so sliderLayoutPositionX + thumbRelativePosX is the Thumb position you're looking for. The same calculation can be done for Y coordinates – user496854 Aug 18 '22 at 03:51
0

Generally, you can determine the position of an element(thumb) within a component(slider) by finding the start and the end of the component(slider), then by using a fraction of the current progress of the component, you could use linear interpolation to determine the exact position. Values of offsetStartExtra and offsetEndExtra variables are are based on experiments just to get the thumb axies right, you may want to change them to other values based on your padding, thumb size, parent position, etc. If needed, play around with them until you get it rigth within your layout.

Slider (Horizontal):

Notice I calculate the end and the start of the slider on onGloballyPositioned

var sliderValue by remember { mutableStateOf(0f) }

var start by remember { mutableStateOf(Offset.Zero) }
var end by remember { mutableStateOf(Offset.Zero) }
var thumbCoordinates by remember { mutableStateOf(Offset.Zero) }

Box(
    modifier = Modifier
        .fillMaxSize()
        .padding(16.dp),
) {
    Slider(
        value = sliderValue,
        onValueChange = { newValue ->
            sliderValue = newValue
            // updating the thumbCoordinate using linear interpolation
            thumbCoordinates = start + (end - start) * (sliderValue / maxSliderValue)
        },
        valueRange = minSliderValue..maxSliderValue,
        modifier = Modifier
            .align(Alignment.Center)
            .onGloballyPositioned { coordinates ->
                // calculating start and end of the slider
                val posInRoot = coordinates.positionInRoot()

                val offsetStartExtra = Offset(x = -28f, y = 10f)
                start = posInRoot + offsetStartExtra


                val offsetEndExtra = Offset(x = -83f, 10f)
                end = posInRoot.copy(posInRoot.x + coordinates.size.width.toFloat()) + offsetEndExtra
                thumbCoordinates=start + (end - start) * (sliderValue/range.endInclusive)

            }
    )

    Canvas(modifier = Modifier.fillMaxSize()) {
        // using the calculated coordinates here...
        drawRect(color = Color.Red, topLeft = thumbCoordinates, size = Size(24f, 24f))
    }

}

Result:

enter image description here

Slider (Vertical)

If you have a vertical slider (check this answer), then you might need to tweak some things for start and end variables as well as the offset extras in onGloballyPositioned modifier.

....
.onGloballyPositioned { coordinates ->
                val posInRoot = coordinates.positionInRoot()

                val startOffsetExtra = Offset(x = 54f, y = -40f)
                start = posInRoot + startOffsetExtra
                
                val endOffsetExtra = Offset(x = 54f, y = 15f)
                end = posInRoot.copy(y = posInRoot.y - coordinates.size.width.toFloat()) + endOffsetExtra
            }
....

Result

enter image description here

Now for your case, you could draw a line with canvas provding startPoint as position of first thumb, and end point the position of the 2nd thumb, then you could draw another line starting from 2nd thumb and ending on the 3rd. Or you could just draw a path with canvas providing all the thumb's positions.

Astro
  • 376
  • 5
  • 11