Jetpack Compose聊天机器人实现消息列表自动滚动到底部
问题:Jetpack Compose LazyColumn 无法自动滚动到最新消息
我正在开发一款聊天机器人应用,采用Jetpack Compose的LazyColumn展示消息列表。列表初始为空,先添加一条Bot消息引导用户交互,后续用户发送消息后会自动添加Bot回复,形成交互循环。当消息数量超出屏幕范围时,希望界面能自动滚动到最新消息位置,但由于列表是动态填充的,且不同设备屏幕尺寸不同,目前无法实现自动滚动,用户需手动滚动,体验较差。截图中可见最新消息(user:dsdssd)未自动显示在可视区域。
相关代码
FirstScreen 代码
@SuppressLint("CoroutineCreationDuringComposition") @Composable fun FirstScreen( viewModel: ChatBotViewModel = hiltViewModel() ) { val list = remember { mutableStateListOf<Message>() } val botList = listOf("Peter", "Francesca", "Luigi", "Igor") val random = (0..3).random() val botName: String = botList[random] val hashMap: HashMap<String, String> = HashMap() viewModel.customBotMessage(message = Message1, list) Column(modifier = Modifier.fillMaxHeight(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Top) { Divider() LazyColumn( modifier = Modifier.fillMaxSize(), ){ items(list.size) { i -> Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = if (list[i].id == RECEIVE_ID) Arrangement.Start else Arrangement.End ) { if (list[i].id == RECEIVE_ID) { Item( message = list[i], botName, botcolor, list, simpleTextFlag = true, hashMap, modifier = Modifier .padding( start = 32.dp, end = 4.dp, top = 4.dp ), ) } else { Item( message = list[i], "user", usercolor, list, simpleTextFlag = false, hashMap, Modifier.padding( start = 4.dp, end = 32.dp, top = 4.dp ) ) } } } } } }
Item 模块代码
@Composable fun Item( message: Message, person: String, color: Color, list: SnapshotStateList<Message>, simpleTextFlag: Boolean, hashMap: HashMap<String, String>, modifier: Modifier, viewModel: ChatBotViewModel = hiltViewModel() ) { Column(verticalArrangement = Arrangement.Top) { Card( modifier = Modifier .padding(10.dp), backgroundColor = color, elevation = 10.dp ) { Row( verticalAlignment = Alignment.Top, modifier = Modifier.padding(3.dp) ) { Text( buildAnnotatedString { withStyle( style = SpanStyle( fontWeight = FontWeight.Medium, color = Color.Black ) ) { append("$person: ") } }, modifier = Modifier .padding(4.dp) ) Text( buildAnnotatedString { withStyle( style = SpanStyle( fontWeight = FontWeight.W900, color = Color.White//Color(/*0xFF4552B8*/) ) ) { append(message.message) } } ) } } if (simpleTextFlag && list.size != 13) { SimpleText(list = list, hashMap, viewModel) } } } @OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class) @Composable fun SimpleText( list: SnapshotStateList<Message>, hashMap: HashMap<String, String>, viewModel: ChatBotViewModel, ) { val coroutineScope = rememberCoroutineScope() val keyboardController = LocalSoftwareKeyboardController.current val bringIntoViewRequester = remember { BringIntoViewRequester() } var visible by remember { mutableStateOf(true) } var text by remember { mutableStateOf("") } AnimatedVisibility( visible = visible, enter = fadeIn() + slideInHorizontally(), exit = fadeOut() + slideOutHorizontally() ) { Column { Row( verticalAlignment = Alignment.Bottom, horizontalArrangement = Arrangement.End, modifier = Modifier.bringIntoViewRequester(bringIntoViewRequester) ) { OutlinedTextField( modifier = Modifier .padding(2.dp) .onFocusEvent { focusState -> if (focusState.isFocused) { coroutineScope.launch { bringIntoViewRequester.bringIntoView() } } }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }), value = text, onValueChange = { text = it }, shape = RoundedCornerShape(12.dp), label = { Text("send message") }) IconButton(onClick = { if (text.isNotEmpty()) { //Adds it to our local list list.add( Message( text, Constants.SEND_ID, Timestamp(System.currentTimeMillis()).toString() ) ) hashMap[Messages.listOfMessageKeys[viewModel.index.value - 1]] = text viewModel.customBotMessage(listOfMessages[viewModel.index.value], list) viewModel.index.value += 1 visible = false } text = "" }) { Icon( modifier = Modifier.padding(2.dp), painter = painterResource(id = R.drawable.ic_baseline_send_24), contentDescription = "send message img" ) } } if (list.size == 3 || list.size == 5 || list.size == 7) { Button() if(viewModel.isClickedBtn.value){ visible = false viewModel.isClickedBtn.value = false list.add( Message( text, Constants.SEND_ID, Timestamp(System.currentTimeMillis()).toString() ) ) hashMap[Messages.listOfMessageKeys[viewModel.index.value - 1]] = text viewModel.customBotMessage(listOfMessages[viewModel.index.value], list) viewModel.index.value += 1 } } } } } @Composable fun Button( viewModel: ChatBotViewModel = hiltViewModel() ) { Button( onClick = { viewModel.isClickedBtn.value = true }, ) { Text("skip") } }
ChatBotViewModel 代码
class ChatBotViewModel @Inject constructor() : ViewModel() { var index = mutableStateOf(value = 1) val isClickedBtn = mutableStateOf(false) fun customBotMessage(message: String, list: SnapshotStateList<Message>) { GlobalScope.launch { delay(1000) withContext(Dispatchers.Main) { list.add( Message( message, Constants.RECEIVE_ID, Timestamp(System.currentTimeMillis()).toString() ) ) } } } }
问题截图

内容的提问来源于stack exchange,提问作者NewPartizal
相关产品推荐
相关产品推荐

