AndroidView inside ComposeView: touch dispatch order between View and Compose pointer input
0 reputation · 27 Mar 2025, 02:44 UTC
0 reputation · 27 Mar 2025, 02:44 UTC
I am designing a screen where a legacy View hierarchy (containing its own touch handling, e.g. a custom map or drawing surface) is embedded in Jetpack Compose via AndroidView, and the surrounding Compose tree also uses pointerInput modifiers for gestures.
The goal is predictable, deterministic touch dispatch: I need to know which side sees a touch event first, and under what conditions the embedded View can consume events before Compose's PointerInputModifier chain runs (and vice versa). Documentation indicates the View system dispatches events first and ComposeView feeds them into Compose's pointer input, but the ordering relative to child Views is not clearly guaranteed, and behavior may vary across Android API levels 21–34. Targeting Compose BOM 2024.x with Kotlin 2.0.x and a matching Compose compiler plugin.
AndroidView's onTouchEvent and a parent Compose pointerInput modifier contractually defined, or implementation-dependent per API level?In a Jetpack Compose hierarchy, the AndroidView acts as a bridge. Because the embedded View remains a member of the traditional Android View system, the embedded View typically sees and processes touch events before the surrounding Compose pointerInput modifiers.
When a touch event occurs, the ComposeView (the root of the Compose tree) receives the event from the Android system. It then dispatches it down to the embedded AndroidView. If the legacy View's onTouchEvent returns true, the event is considered consumed by the View system and will not trigger the Compose pointerInput chain for that specific event stream.
To manage the boundary between the two systems, use these established Android View mechanisms:
true from onTouchEvent. This signals to the parent ComposeView that the event has been handled.false from onTouchEvent. This allows the event to bubble back up to the Compose pointer input system.AndroidView is nested inside a Compose scrollable container (like LazyColumn), the View should call requestDisallowInterceptTouchEvent(true) on its parent. This prevents the Compose-based scroll container from stealing the gesture stream once the View has started tracking a gesture.Logging touch coordinates and event types is the standard and accepted way to verify this ordering. There is no public "test hook" API for the internal ComposeView dispatch logic. To verify your specific implementation, implement the following:
Log.d statement inside the onTouchEvent of your custom View.Log.d statement inside the awaitPointerEventScope of your Compose pointerInput modifier.This behavior is consistent across API levels 21–34 because the bridge relies on the fundamental View.onTouchEvent contract. However, this assumes the AndroidView is not wrapped in a Box or other Compose layout that has already consumed the pointer event via a pointerInput modifier higher up in the tree, as Compose's own internal propagation (downward then upward) occurs before the event is handed off to the legacy View system.
Missing Diagnostic: Are you using a scrollable Compose container (e.g., Modifier.verticalScroll) as a parent to the AndroidView? If so, the parent may intercept the event before it ever reaches the embedded View.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.