I have a vertical Column
that has two children:
- A
Pager
composable (from the Accompanist), which displaysText
. - A
Canvas
composable that draws two arcs, and a circle.
They are ordered in the same order described here. I want the Text
to always render behind the Canvas
.
Implementation Details
The Canvas
composable has a Modifier.pointerInteropFilter
that handles MotionEvent.ACTION_DOWN
and MotionEvent.ACTION_MOVE
. If the event is not within 10% distance of the arc, then it is ignored, and false
returned.
My understanding was returning false
would indicate that the event has not been consumed, and therefore would be passed through for the Pager
to handle - but this is not the case. Once the Modifier.pointerInteropFilter
has been registered, the Pager
never receives input events, regardless of returning true
or false
.
Layout
Below is a reduced version of my current implementation, for brevity. I have a Column
that renders the Pager
first, then the Canvas
.
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center
) {
// The Pager should always be rendered behind the Canvas. This is to
// prevent the Text from rending on top when being dragged.
HorizontalPager(
state = rememberPagerState(pageCount = 8000, initialPage = 4000)
) { page ->
Text(
modifier = Modifier.fillMaxWidth(),
text = "25:00",
textAlign = TextAlign.Center,
fontSize = 60.sp
)
}
}
var progress by remember { mutableStateOf(0.25f) }
ArcSeekBar(progress, false) { change ->
progress = change
}
In this example, I have hardcoded the pointerInteropFilter
to always return false, under the assumption that it would not consume/block the event, allowing it to pass through to the Pager
.
@Composable
fun ArcSeekBar(progress: Float, interaction: Boolean, changed: (Float) -> Unit) {
val sweepAngle = 270.0f
var width: Int by remember { mutableStateOf(0) }
var height: Int by remember { mutableStateOf(0) }
Canvas(
modifier = Modifier
.fillMaxSize()
.aspectRatio(1.0f)
.onSizeChanged {
width = it.width
height = it.height
}
.pointerInteropFilter {
// Reduced for brevity, but whatever the function returns
// the Pager composable behind it will never receive input events.
false
},
) {
// Omitted for brevity, draw two arcs and a circle
}
}
For reference, my layout produces the following:
HorizontalPager
so that it does not overlap the progress bar area. Looks visually poor when dragging the pager text as it clips. – Giggle