【问题标题】:Build Software Keyboard with Jetpack Compose - IME Input Method with Jetpack Compose使用 Jetpack Compose 构建软件键盘 - 使用 Jetpack Compose 的 IME 输入法
【发布时间】:2021-04-10 16:18:50
【问题描述】:

在 Jetpack Compose 中构建一个简单的键盘相当简单明了。 我使用这个构建了一个非常简单的 KeyRow:

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 = {  })
        }
    }
}

它看起来是这样的:

我要实现这个动画:

但是,我目前对此感到困惑

![4]

层次结构

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

我的主要问题是我不知道如何在不增大父布局的情况下显示 KeyPressedOverlay Composale(大于 Key Composable)。结果,我需要以某种方式溢出父布局。

【问题讨论】:

    标签: android android-jetpack-compose


    【解决方案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
            )
        }
    }
    

    这里的一个重要细节是graphicLayer(clip = false)(类似于 View Toolkit 中的clipChildren)。然后,我为每个键和按下的键创建一个修饰符。注意到modifierPressed 与另一个修饰符的中心/底部对齐。 最后,KeyboardKey 描述如下。

    @Composable
    fun KeyboardKey(
        keyboardKey: String,
        modifier: Modifier,
        modifierPressed: Modifier
    ) {
        var pressed by remember { mutableStateOf(false) }
        Text(keyboardKey, Modifier
            .then(modifier)
            .pressIndicatorGestureFilter(
                onStart = { pressed = true },
                onStop = { pressed = false },
                onCancel = { pressed = false }
            )
            .background(Color.White)
            .padding(16.dp)
        )
        if (pressed) {
            Text(
                keyboardKey, Modifier
                    .then(modifierPressed)
                    .background(Color.White)
                    .padding(
                        start = 16.dp, 
                        end = 16.dp, 
                        top = 16.dp, 
                        bottom = 48.dp
                    )
            )
        }
    }
    

    这是我得到的结果:

    编辑: 添加更多逻辑,我能够得到这个......

    希望这次能有所帮助;) 这是要点以防万一... https://gist.github.com/nglauber/4cb1573efba9024c008ea71f3320b4d8

    【讨论】:

    • 感谢您的回答!使用平面布局层次结构的好主意。有点hacky,但它有效:)
    • 有没有办法通过自定义布局来实现这一点? ConstraintLayout 在复杂情况下会变得非常慢
    • 我能够实现“重叠”行为,而无需使用复杂的约束(在我的情况下,它变得如此缓慢,所以我无法使用它)使用具有固定的简单自定义布局身高gist.github.com/THEAccess/b71e17d58d830ed6deaed7066a929576
    • 唯一的问题是在下方而不是上方展开
    • 我有一个完整的应用程序,所以你可以在这里看到我的意思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)
    )
    

    【讨论】:

    • 感谢您的回答。我已经使用interactionState 归档了点击行为。我已经更新了我的问题,以便更清楚地了解我的特定问题
    猜你喜欢
    • 2022-10-25
    • 1970-01-01
    • 1970-01-01
    • 2022-12-18
    • 1970-01-01
    • 2022-12-22
    • 1970-01-01
    • 2021-11-16
    • 2022-01-07
    相关资源
    最近更新 更多