Как управлять состоянием фокуса в Jetpack's Compose - PullRequest
1 голос
/ 23 апреля 2020

У меня есть настраиваемый составной вид (по существу, поверхность + текст), и я хочу изменить цвет поверхности в зависимости от состояния фокуса. FocusManager # FocusNode помечен как внутренний, и я не знаю, как этого добиться. Это просто просто еще не доступно? Кто-нибудь еще должен заняться этим?

1 Ответ

1 голос
/ 09 мая 2020

Compose с тех пор обновил и сделал FocusManager членов public; хотя, я не совсем уверен, насколько окончательный API на dev10. Но на данный момент вы можете создать FocusNode и прослушать изменения, используя FocusManager.registerObserver.

val focusNode = remember {
    FocusNode().apply {
        focusManager.registerObserver(node = this) { fromNode, toNode ->
            if (toNode == this) {
                // has focus
            } else {
                // lost focus
            }
        }
    }
}

Если вы хотите получить фокус, вы можете позвонить FocusManager.requestFocus:

val focusManager = FocusManagerAmbient.current
focusManager.requestFocus(focusNode)

Вы также можете установить focusIdentifier на FocusNode:

val focusNode = remember {
    FocusNode().apply {
        ...
        focusManager.registerFocusNode("your-focus-identifier", node = this)
    }
}

Чтобы получить фокус для указанного c идентификатора, вы просто позвоните FocusManager.requestFocusById

С помощью этого вы можете легко создать Composable, который может предоставить и запросить фокус для вас, например:

@Composable
fun useFocus(focusIdentifier: String? = null): Pair<Boolean, () -> Unit> {
    val focusManager = FocusManagerAmbient.current
    val (hasFocus, setHasFocus) = state { false }
    val focusNode = remember {
        FocusNode().apply {
            focusManager.registerObserver(node = this) { fromNode, toNode ->
                setHasFocus(toNode == this)
            }
            focusIdentifier?.let { identifier ->
                focusManager.registerFocusNode(identifier, node = this)
            }
        }
    }
    onDispose {
        focusIdentifier?.let { identifier ->
            focusManager.unregisterFocusNode(identifier)
        }
    }
    return hasFocus to {
        focusManager.requestFocus(focusNode)
    }
}
val (hasFocus, requestFocus) = useFocus("your-focus-identifier")

Вы также можете составить детей вместе с ним:

@Composable
fun FocusableTextButton(
    text: String,
    focusedColor: Color = Color.Unset,
    unFocusedColor: Color = Color.Unset,
    textColor: Color = Color.White,
    focusIdentifier: String? = null
) {
    val (hasFocus, requestFocus) = useFocus(focusIdentifier)
    Surface(color = if (hasFocus) focusedColor else unFocusedColor) {
        TextButton(onClick = requestFocus) {
            Text(text = text, color = textColor)
        }
    }
}

В качестве альтернативы, есть также FocusModifier, который на данный момент:

/**
 * A [Modifier.Element] that wraps makes the modifiers on the right into a Focusable. Use a
 * different instance of [FocusModifier] for each focusable component.
 *
 * TODO(b/152528891): Write tests for [FocusModifier] after we finalize on the api (API
 * review tracked by b/152529882).
 */

Но я не думаю, что вы можете применить идентификатор с ним прямо сейчас.

val focusModifier = FocusModifier()
val hasFocus = focusModifier.focusDetailedState == FocusDetailedState.Active
Surface(
    modifier = focusModifier,
    color = if (hasFocus) focusedColor else unFocusedColor
) {
    TextButton(onClick = { focusModifier.requestFocus() }) {
        Text(text = text, color = textColor)
    }
}

Несмотря на это, я не уверен на 100%, что это намеченный способ обработки сосредоточиться прямо сейчас. Я много ссылался на CoreTextField , чтобы посмотреть, как он там обрабатывается.

Пример:

@Composable
fun FocusTest() {
    val focusManager = FocusManagerAmbient.current
    val selectRandomIdentifier: () -> Unit = {
        focusManager.requestFocusById(arrayOf("red,", "blue", "green", "yellow").random())
    }
    Column(verticalArrangement = Arrangement.SpaceBetween) {
        FocusableTextButton(
            text = "When I gain focus, I turn red",
            focusedColor = Color.Red,
            focusIdentifier = "red"
        )
        FocusableTextButton(
            text = "When I gain focus, I turn blue",
            focusedColor = Color.Blue,
            focusIdentifier = "blue"
        )
        FocusableTextButton(
            text = "When I gain focus, I turn green",
            focusedColor = Color.Green,
            focusIdentifier = "green"
        )
        FocusableTextButton(
            text = "When I gain focus, I turn yellow",
            focusedColor = Color.Yellow,
            focusIdentifier = "yellow"
        )
        Button(onClick = selectRandomIdentifier) {
            Text(text = "Click me to randomly select a node to focus")
        }
    }
}

gif

...