Сборка программной клавиатуры с Jetpack Compose — IME метод ввода с Jetpack Compose

#android #android-jetpack-compose

#Android #android-jetpack-compose

Вопрос:

Создание простой клавиатуры в Jetpack Compose довольно простое и понятное. Я создал действительно простой набор клавиш, используя это:

Key.kt

 @Composable
fun Key(modifier: Modifier = Modifier, label: String, onClick: () -> Unit) {
    val shape = RoundedCornerShape(4.dp)
    //TODO: make clickable outside but don't show ripple
    Box(modifier = modifier
            .padding(2.dp)
            .clip(shape)
            .clickable(onClick = onClick)
            .background(Color.White)
            .padding(vertical = 12.dp, horizontal = 4.dp), contentAlignment = Alignment.Center) {
        Text(text = label, fontSize = 20.sp)
    }
}
 

KeyRow.kt

 @Composable
fun KeyRow(keys: List<String>) {
    Row(modifier = Modifier.fillMaxWidth().background(color = grey200)) {
        keys.forEach {
            Key(modifier = Modifier.weight(1f), label = it, onClick = {  })
        }
    }
}
 

Вот как это выглядит:

2

Я хочу добиться этой анимации:

3

Однако в настоящее время я застрял с этим

![4]

Иерархия

 -Keyboard
--KeyRow
---KeyLayout
----Key
----KeyPressedOverlay (only visible when pressed)
 

Моя главная проблема в том, что я не знаю, как отобразить композицию KeyPressedOverlay (которая больше, чем составная клавиша), не увеличивая родительский макет. В результате мне нужно каким-то образом переполнить родительский макет.

Ответ №1:

Не уверен, что это лучший способ (возможно, нет), но я нашел решение с помощью ConstraintLayout

 val keys = listOf("A", "B", "C", "D")
ConstraintLayout(
    modifier = Modifier.graphicsLayer(clip = false)
) {
    val refs = keys.map { createRef() }
    refs.forEachIndexed { index, ref ->
        val modifier = when (index) {
            0 -> Modifier.constrainAs(ref) {
                start.linkTo(parent.start)
            }
            refs.lastIndex -> Modifier.constrainAs(ref) {
                start.linkTo(refs[index - 1].end)
                end.linkTo(parent.end)
            }
            else -> Modifier.constrainAs(ref) {
                start.linkTo(refs[index - 1].end)
                end.linkTo(refs[index   1].start)
            }
        }
        val modifierPressed = Modifier.constrainAs(createRef()) {
            start.linkTo(ref.start)
            end.linkTo(ref.end)
            bottom.linkTo(ref.bottom)
        }
        KeyboardKey(
            keyboardKey = keys[index],
            modifier = modifier,
            modifierPressed = modifierPressed,
            pressed = { s -> /* Do something with the key */}
        )
    }
}
 

Здесь есть одна важная деталь graphicLayer(clip = false) (которая похожа на clipChildren in View Toolkit). Затем я создаю модификатор для каждой клавиши и для нажатой клавиши. Заметил, что modifierPressed он выровнен по центру / низу другого модификатора.
Наконец KeyboardKey , это описано ниже.

 @Composable
fun KeyboardKey(
    keyboardKey: String,
    modifier: Modifier,
    modifierPressed: Modifier,
    pressed: (String) -> Unit
) {
    var isKeyPressed by remember { mutableStateOf(false) }
    Text(keyboardKey, Modifier
        .then(modifier)
        .pointerInput(Unit) {
            detectTapGestures(onPress = {
                isKeyPressed = true
                val success = tryAwaitRelease()
                if (success) {
                    isKeyPressed = false
                    pressed(keyboardKey)
                } else {
                    isKeyPressed = false
                }
            })
        }
        .background(Color.White)
        .padding(
            start = 12.dp,
            end = 12.dp,
            top = 16.dp,
            bottom = 16.dp
        ),
        color = Color.Black
    )
    if (isKeyPressed) {
        Text(
            keyboardKey, Modifier
                .then(modifierPressed)
                .background(Color.White)
                .padding(
                    start = 16.dp,
                    end = 16.dp,
                    top = 16.dp,
                    bottom = 48.dp
                ),
            color = Color.Black
        )
    }
}
 

Вот результат, который я получил:
Эффект клавиатуры

Редактировать: добавив еще немного логики, я смог получить это… введите описание изображения здесь

Надеюсь, на этот раз это поможет 😉 Вот суть на всякий случай … https://gist.github.com/nglauber/4cb1573efba9024c008ea71f3320b4d8

Комментарии:

1. Спасибо за ваш ответ! Отличная идея использовать иерархию плоской компоновки. Немного халтурно, но это работает 🙂

2. Есть ли способ добиться этого с помощью пользовательских макетов? ConstraintLayout может работать очень медленно в сложных ситуациях

3. Я смог добиться «перекрывающегося» поведения без необходимости использования сложных ограничений (в моем случае это происходило так медленно, что я не мог его использовать), используя простой пользовательский макет с фиксированной высотой gist.github.com/THEAccess/b71e17d58d830ed6deaed7066a929576

4. Единственная проблема заключается в том, что он расширяется ниже, а не выше

5. У меня есть полное приложение, так что вы можете понять, что я имею в виду здесь github.com/THEAccess/compose-keyboard-ime/blob/master/app/src /… и gif-файл imgur.com/a/GGL2ziI может быть, вы знаете, как решить эту маленькую проблему

Ответ №2:

Я думаю, вы ищете pressIndicatorGestureFilter модификатор… Я попробовал это и сработало для меня…

 var pressed by remember { mutableStateOf(false) }
val padding = if (pressed) 32.dp else 16.dp
Text("A", Modifier
    .pressIndicatorGestureFilter(
        onStart = {
            pressed = true
        },
        onStop = {
            pressed = false
        },
        onCancel = {
            pressed = false
        }
    )
    .background(Color.White)
    .padding(start = 16.dp, end = 16.dp, top = padding, bottom = padding)
)
 

Комментарии:

1. Спасибо за ваш ответ. Я архивировал поведение щелчка с interactionState помощью . Я обновил свой вопрос, чтобы прояснить мою конкретную проблему