'OnKeyEvent without focus in Jetpack Compose
I am creating an app that makes use of a physical button on the device.
This button will have a different functionality depending on the screen that is active.
With Activities what I would do would be to have an Activity for each screen and in each one I would override the onKeyDown function. How would I do this with a single activity that navigates between different Jetpack Compose screens? According to the Android documentation the correct way would be something like this...
Box(modifier = Modifier
.onKeyEvent {
Log.e("Pressed", it.nativeKeyEvent.keyCode.toString())
true
}
.focusable()
.fillMaxSize()
.background(Color.Gray)
) {
// All screen components
}
But this only works when one of the elements on the screen is focused and what I require is that it always works or not, is there a way to achieve this?
Solution 1:[1]
One option is to keep the focus on the view so that the Modifier.onKeyEvent
always works. Note that it may conflict with other focusable views, like TextField
, so all these views should be children(at any level) of this always-focusable view.
val focusRequester = remember { FocusRequester() }
var hasFocus by remember { mutableStateOf(false) }
Box(
Modifier
.focusRequester(focusRequester)
.onFocusChanged {
hasFocus = it.hasFocus
}
.focusable()
.onKeyEvent {
TODO()
}
) {
}
if (!hasFocus) {
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
}
Another option is to create compositional local handlers and pass events from your activity:
class MainActivity : AppCompatActivity() {
private val keyEventHandlers = mutableListOf<KeyEventHandler>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CompositionLocalProvider(LocalKeyEventHandlers provides keyEventHandlers) {
// your app
}
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
return keyEventHandlers.reversed().any { it(keyCode, event) } || super.onKeyDown(keyCode, event)
}
}
val LocalKeyEventHandlers = compositionLocalOf<MutableList<KeyEventHandler>> {
error("LocalKeyEventHandlers is not provided")
}
typealias KeyEventHandler = (Int, KeyEvent) -> Boolean
@Composable
fun ListenKeyEvents(handler: KeyEventHandler) {
val handlerState = rememberUpdatedState(handler)
val eventHandlers = LocalKeyEventHandlers.current
DisposableEffect(handlerState) {
val localHandler: KeyEventHandler = {
handlerState.value(it)
}
eventHandlers.add(localHandler)
onDispose {
eventHandlers.remove(localHandler)
}
}
}
Usage:
ListenKeyEvents { code, event ->
TODO()
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |