您的位置:首页 > 新闻 > 热点要闻 > 广告加工厂_长沙网站设计公司重庆标志_免费站长统计工具_娄底地seo

广告加工厂_长沙网站设计公司重庆标志_免费站长统计工具_娄底地seo

2025/3/10 23:17:52 来源:https://blog.csdn.net/weixin_43240212/article/details/146029120  浏览:    关键词:广告加工厂_长沙网站设计公司重庆标志_免费站长统计工具_娄底地seo
广告加工厂_长沙网站设计公司重庆标志_免费站长统计工具_娄底地seo

此前发过一篇文章介绍了我开发的Desktop端端跨平台Android设备调试软件——DebugManager。

包含了基础设备信息,应用管理,文件管理,性能监测,主题切换等。

本次记录问题点

记录为开发AI大模型对话功能页面中,对TextField输入框回车键监听问题的解决。

页面如下:

普通用户在电脑程序中对于输入框的期望,就是按Enter键可以直接确认,按Alt+Enter可以输入换行符。

第一版——基础输入功能

对官方TextField进行简单封装:

@Composable
fun WrappedEditText(value: String,onValueChange: (String) -> Unit,tipText: String,modifier: Modifier = Modifier
) {TextField(value = value,textStyle = infoText,colors = TextFieldDefaults.textFieldColors(textColor = MaterialTheme.colors.onPrimary,cursorColor = MaterialTheme.colors.onPrimary,focusedIndicatorColor = MaterialTheme.colors.onPrimary,unfocusedIndicatorColor = MaterialTheme.colors.onSecondary),label = { Text(tipText, color = MaterialTheme.colors.onSecondary) },onValueChange = { onValueChange(it) },modifier = modifier.widthIn(max = 200.dp, min = 100.dp).clip(RoundedCornerShape(10.dp)).background(MaterialTheme.colors.secondary).border(2.dp, MaterialTheme.colors.onSecondary, RoundedCornerShape(10.dp)),)
}

外部使用通过维护一个mutableStringState,和这里的onValueChange来进行TextField显示内容和实际字符串变量的更新。

@Composable
fun AiModelPage() {BasePage("AI大模型对话") {val mainStateHolder by remember { mutableStateOf(GlobalContext.get().get<MainStateHolder>()) }val toastState = rememberToastState()val userInputSting = remember { mutableStateOf("") }WrappedEditText(value = userInputSting.value,tipText = "输入对话文字",onValueChange = { userInputSting.value = it },modifier = Modifier.padding(start = 10.dp, end = 10.dp).weight(1f),)CommonButton("发送", onClick = {if (userInputSting.value.isEmpty()) {toastState.show("请先输入对话内容")} else {mainStateHolder.chatWithAI(userInputSting.value)userInputSting.value = ""}},modifier = Modifier.padding(10.dp))}
}

只有点击来发送按钮后,才会将对话内容发给大模型。

第二版——加入Enter事件回调

这时候加入了KeyEvent的监听:

@Composable
fun WrappedEditText(value: String,onValueChange: (String) -> Unit,tipText: String,modifier: Modifier = Modifier,onEnterPressed: () -> Unit = {}
) {val focusRequester = remember { FocusRequester() }TextField(value = value,textStyle = infoText,colors = TextFieldDefaults.colors(focusedTextColor = MaterialTheme.colorScheme.onPrimary,cursorColor = MaterialTheme.colorScheme.onPrimary,focusedIndicatorColor = MaterialTheme.colorScheme.onPrimary,unfocusedIndicatorColor = MaterialTheme.colorScheme.onPrimary),label = { Text(tipText, color = MaterialTheme.colorScheme.onSecondary) },onValueChange = { onValueChange(it) },modifier = modifier.widthIn(max = 200.dp, min = 100.dp).clip(RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.secondary).border(2.dp, MaterialTheme.colorScheme.onSecondary, RoundedCornerShape(10.dp)).focusRequester(focusRequester).onKeyEvent {if (it.key == Key.Enter) {onEnterPressed()return@onKeyEvent true}false},)
}

在监测到Enter键按下时,执行外部的onEnterPressed这个Lambda块,外部调用配置的时候,执行和点击发送按钮一样的逻辑。

问题就是,最后的这个换行符,连同输入的内容一起被添加到了输入框的UI,还有对话气泡中去了。

第三版——AI提供的传参数方案

查看官方文档,提供的几个api都会和上面那个按键监听策略一样的问题,换行符和内容混到了一起。

询问Gemini给出了一个方法,通过自定义`keyboardOptions`,`keyboardActions`两个参数,在onDone回调里调用onEnterPressed代码块。

@Composable
fun WrappedEditText(value: String,onValueChange: (String) -> Unit,tipText: String,modifier: Modifier = Modifier,onEnterPressed: () -> Unit = {}
) {val focusRequester = remember { FocusRequester() }TextField(value = value,textStyle = infoText,colors = TextFieldDefaults.colors(focusedTextColor = MaterialTheme.colorScheme.onPrimary,cursorColor = MaterialTheme.colorScheme.onPrimary,focusedIndicatorColor = MaterialTheme.colorScheme.onPrimary,unfocusedIndicatorColor = MaterialTheme.colorScheme.onPrimary),label = { Text(tipText, color = MaterialTheme.colorScheme.onSecondary) },onValueChange = { onValueChange(it) },keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),keyboardActions = KeyboardActions(onDone = {onEnterPressed()}),modifier = modifier.widthIn(max = 200.dp, min = 100.dp).clip(RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.secondary).border(2.dp, MaterialTheme.colorScheme.onSecondary, RoundedCornerShape(10.dp)).focusRequester(focusRequester),)
}

发现并没有监听到Enter键的事件。

恢复到第二版的方案后,通过在 `onValueChange` 和 `onKeyEvent` 里打印log看到,在普通按键按下时,KeyEvent可以拦截,先手回调。然而按下Enter键时,KeyEvent却在onValueChange后面回调,这样就无法提前对onValueChange回调之前进行操作。

第四版——使用内部状态来多重判断

先上代码:

@Composable
fun WrappedEditText(value: String,onValueChange: (String) -> Unit,tipText: String,modifier: Modifier = Modifier,onEnterPressed: () -> Unit = {}
) {val focusRequester = remember { FocusRequester() }var ctrlPressed by remember { mutableStateOf(false) }var altPressed by remember { mutableStateOf(false) }TextField(value = value,textStyle = infoText,colors = TextFieldDefaults.colors(focusedTextColor = MaterialTheme.colorScheme.onPrimary,cursorColor = MaterialTheme.colorScheme.onPrimary,focusedIndicatorColor = MaterialTheme.colorScheme.onPrimary,unfocusedIndicatorColor = MaterialTheme.colorScheme.onPrimary),label = { Text(tipText, color = MaterialTheme.colorScheme.onSecondary) },onValueChange = {// 如果此时使用了ctrl或者alt键,那么就不做处理// 否则就处理,丢弃掉最后一个换行符onValueChange(if (!ctrlPressed && !altPressed) it.processText() else it)},modifier = modifier.widthIn(max = 200.dp, min = 100.dp).clip(RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.secondary).border(2.dp, MaterialTheme.colorScheme.onSecondary, RoundedCornerShape(10.dp)).focusRequester(focusRequester).onKeyEvent {// 只有单独按下enter键才触发,其余组合键只换行if (it.isCtrlPressed) {ctrlPressed = truereturn@onKeyEvent false} else {ctrlPressed = false}if (it.isAltPressed) {altPressed = truereturn@onKeyEvent false} else {altPressed = false}if (it.key == Key.Enter) {onEnterPressed()return@onKeyEvent true}false},)
}/*** 用来兜底TextField的bug,暂时没有找到更好的解决方案* 手动丢弃掉最后一个换行符*/
private fun String.processText(): String {return if (this.endsWith("\n")) {// 如果是单一个换行符,直接置空// 如果非单换行符,就丢弃最后一个字符if (this.length == 1) ""else this.dropLast(1)} else this
}

KeyEvent里面提供了几个重要按键按下的状态回调,我使用内部State来记录Ctrl和Alt这两个按键的按键状态,isPressed时置为true,没有按下时置为false,这样就可以在onValueChange时对回调过来的字符串进行加工处理。即,在Ctrl按键和Alt按键按下时,如实地回调键盘事件给输入框,这两个按键都没有按时,对字符串的最后一个字符进行检查。

处理方法如 `String.processText()`,如果以换行符结尾,判断这个zfc是不是就只有一个换行符,这种情况就直接置为空字符串,如果有多个字符,就把最后一个换行符给去掉,再传递给外部的调用方,保证了输入框的UI和实际的字符串里都不会显示异常。

最终实现组合按键正常换行,单独换行键直接发送对话。后续计划持续跟进,看看这里是不是跨平台库中的一个BUG,还有就是有没有官方封装完善的方案来直接使用。
 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com