Jetpack Compose:TextField 在点击它外部时不会失去焦点

8
我正在尝试在我的Compose应用中实现一个搜索栏。我有一个文本字段,当我在文本字段之外点击时,我希望它失去焦点并隐藏键盘。但是,我无法弄清楚如何实现。我尝试过使用在GitHub上看到的代码,但进行了一些更改。
然而,原始解决方案使用了这部分代码isHintDisplayed = it != FocusState.Active,似乎已经过时了。所以我将其更改为isHintDisplayed = it.isFocused != true,理论上应该能达到同样的效果。如果我回到较旧版本的compose并使用FocusState.Active,则聚焦/取消聚焦就完美工作,但我无法让它与我的代码配合使用。
有什么想法吗?
以下是我的代码:
@Composable
fun ListScreen(
    navController: NavController
) {
    Surface(
        color = MaterialTheme.colors.background,
        modifier = Modifier.fillMaxSize()
    ) {
        Column {
            Spacer(modifier = Modifier.height(20.dp))
            Image(
                painter = painterResource(id = R.drawable.ic_international_pok_mon_logo),
                contentDescription = "Cards",
                modifier = Modifier
                    .fillMaxWidth()
                    .align(CenterHorizontally)
            )
            SearchBar(
                hint = "Search",
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp)
            ) {

            }
        }
    }
}

这里是搜索栏:

@Composable
fun SearchBar(
    modifier: Modifier = Modifier,
    hint: String = "",
    onSearch: (String) -> Unit = {}
) {
    var text by remember {
        mutableStateOf("")
    }
    var isHintDisplayed by remember {
        mutableStateOf(false)
    }

    Box(modifier = modifier) {
        BasicTextField(
            value = text,
            onValueChange = {
                text = it
                onSearch(it)
            },
            maxLines = 1,
            singleLine = true,
            textStyle = TextStyle(color = Color.Black),
            modifier = Modifier
                .fillMaxWidth()
                .shadow(5.dp, CircleShape)
                .background(Color.White, CircleShape)
                .padding(horizontal = 20.dp, vertical = 12.dp)
                .onFocusChanged {
                    isHintDisplayed = !it.isFocused
                }
        )
        if(isHintDisplayed) {
            Text(
                text = hint,
                color = Color.LightGray,
                modifier = Modifier
                    .padding(horizontal = 20.dp, vertical = 12.dp)
            )
        }
    }
}

潜在的重复问题 https://dev59.com/yVEG5IYBdhLWcg3wRIed - rileyx
这个回答解决了你的问题吗?android compose textfield如何在触摸外部时关闭键盘 - rileyx
1个回答

7

如果你想要隐藏键盘并清除焦点,可以在你的TextField中添加KeyboardOptionsKeyboardActions。你可以添加任何你想要的ImeAction,例如ImeAction.Search
如果你想要在点击外部区域时隐藏键盘,那么你可以在父组件中设置一个可点击的修饰符,并通过一个变量来管理键盘的显示和隐藏状态,该变量的状态将在点击父组件时改变。
如果你想通过键盘操作和外部区域点击来管理键盘显示选项和焦点状态,可以按照以下方式修改你的代码。

@Composable
fun ListScreen() {
    Surface(
        color = MaterialTheme.colors.background,
        modifier = Modifier.fillMaxSize()
    ) {
        var hideKeyboard by remember { mutableStateOf(false) }
        Column(modifier = Modifier.clickable { hideKeyboard = true }) {
            Spacer(modifier = Modifier.height(20.dp))
            Image(
                painter = painterResource(id = R.drawable.ic_launcher_foreground),
                contentDescription = "Cards",
                modifier = Modifier
                    .fillMaxWidth()
                    .align(CenterHorizontally)
            )
            SearchBar(
                hint = "Search",
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp),
                hideKeyboard = hideKeyboard,
                onFocusClear = { hideKeyboard = false }
            ) {

            }
        }
    }
}


@Composable
fun SearchBar(
    modifier: Modifier = Modifier,
    hint: String = "",
    hideKeyboard: Boolean = false,
    onFocusClear: () -> Unit = {},
    onSearch: (String) -> Unit = {}
) {
    var text by remember {
        mutableStateOf("")
    }
    var isHintDisplayed by remember {
        mutableStateOf(false)
    }
    val focusManager = LocalFocusManager.current

    Box(modifier = modifier.background(color = Color.Red)) {
        BasicTextField(
            value = text,
            onValueChange = {
                text = it
                onSearch(it)
            },
            keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
            keyboardActions = KeyboardActions(onSearch = {
                focusManager.clearFocus()
                onSearch(text)
            }),
            maxLines = 1,
            singleLine = true,
            textStyle = TextStyle(color = Color.Black),
            modifier = Modifier
                .fillMaxWidth()
                .shadow(5.dp, CircleShape)
                .background(Color.White, CircleShape)
                .padding(horizontal = 20.dp, vertical = 12.dp)
                .onFocusChanged {
                    isHintDisplayed = !it.hasFocus
                }
        )
        if(isHintDisplayed) {
            Text(
                text = hint,
                color = Color.LightGray,
                modifier = Modifier
                    .padding(horizontal = 20.dp, vertical = 12.dp)
            )
        }
    }

    if (hideKeyboard) {
        focusManager.clearFocus()
        // Call onFocusClear to reset hideKeyboard state to false
        onFocusClear()
    }
}

在您的父组件中设置可点击修饰符的第二个选项,难道不会出现大量代码重复吗?比如说,在应用程序的不同部分中有一个可重用的文本字段,在某些地方我们有一个脚手架,在其他地方则是一个卡片在脚手架上? - simon
1
@simon 这个答案是根据 OP 的需求提供的。在其他情况下,我们可以使用提升或集中控制来消除代码重复。 - Ahsan Ullah Rasel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接