Timo Drick
03/11/2021, 12:27 PMTimo Drick
03/11/2021, 12:27 PMAndrey Kulikov
03/11/2021, 1:36 PMTimo Drick
03/11/2021, 2:11 PM@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun TvStart(googleSignIn: KGoogleSignIn, onClick: () -> Unit) {
val signedIn by googleSignIn.signedIn
val (signInOutBtn, startBtn) = FocusRequester.createRefs()
DisposableEffect(Unit) {
signInOutBtn.requestFocus()
onDispose { }
}
Column {
val onSignInClick = {
if (signedIn) {
googleSignIn.signOut()
} else {
googleSignIn.signIn()
}
}
ClickableBox(
modifier = Modifier
.focusOrder(signInOutBtn) {
next = startBtn
down = startBtn
},
onClick = onSignInClick
) { isFocused ->
TextButton(onClick = onSignInClick) {
val buttonText = if (signedIn) "Sign out" else "Sign in"
Text(buttonText, color = if (isFocused) Color.White else Color.Blue)
}
}
if (signedIn) {
ClickableBox(
modifier = Modifier.focusOrder(startBtn) {
previous = signInOutBtn
up = signInOutBtn
},
onClick = onClick
) { isFocused ->
TextButton(onClick = onClick) {
Text("Check photos", color = if (isFocused) Color.White else Color.Blue)
}
}
}
}
}
@Composable
private fun ClickableBox(
modifier: Modifier = Modifier,
onClick: (() -> Unit)? = null,
content: @Composable BoxScope.(isFocused: Boolean) -> Unit
) {
var isFocused by remember { mutableStateOf(false) }
Box(
modifier = modifier
.onKeyEvent {
log("$it")
if (it.type == KeyEventType.KeyUp && (it.key == Key.Enter || it.key == Key.DirectionCenter)) {
onClick?.invoke()
true
} else {
false
}
}
.onFocusChanged { isFocused = it.isFocused }
.focusModifier(),
content = { content(isFocused) }
)
}
Timo Drick
03/15/2021, 9:54 AMWalter Berggren
03/15/2021, 9:56 AMWalter Berggren
03/15/2021, 1:13 PMTimo Drick
03/15/2021, 1:54 PMWalter Berggren
03/15/2021, 4:15 PMmodifier = Modifier
.focusOrder(tapOutside) {
up = initial
right = initial
down = initial
left = initial
}
By initializing focus to the root element, nothing is focused. When the user clicks an arrow, focus is moved to the initial element.
I then added a .pointerInput
listener to the root element to recapture focus to the root element whenever the user taps somewhere where focus isn’t captured. I received an FocusRequester is not initialized
error if I ran requestFocus directly from pointerInput
, so I used a mutable state variable to run it with the DisposableEffect
composable:
var isDpadNavigationHidden by remember { mutableStateOf(true) }
...
.pointerInput(Unit) {
detectTapGestures {
isDpadNavigationHidden = true
}
}
...
DisposableEffect(isDpadNavigationHidden) {
if (isDpadNavigationHidden) {
tapOutside.requestFocus()
isDpadNavigationHidden = false
}
onDispose { }
}
Walter Berggren
03/15/2021, 5:41 PM