автоматическая прокрутка экрана до последнего элемента джетпака
Я делаю приложение для чат-бота. Я искал во многих местах, чтобы решить эту проблему, но не мог найти ничего ясного. У меня пустой список. В этот список добавляются сообщения ботов и сообщения пользователей, но сначала он пуст. Я показываю этот список с помощью lazycolumn. Так как сначала он пустой, на экран не выводится сообщение, но сначала я показал сообщение бота на экране, чтобы пользователь мог взаимодействовать с пользователем, и в ответ пользователь вводит сообщение, если пользователь вводит сообщение, я снова показываю сообщение бота, и таким же образом пользователь отвечает на это сообщение бота. вводит сообщение. Короче говоря, есть такой цикл, но я хочу, чтобы экран прокручивался, когда сообщения достигают конца экрана. Я не мог найти, как это сделать, потому что я не Список определенного размера сначала заполняется пустыми ответами пользователей и сообщениями ботов. Во втором вопросе, на мой взгляд, размер экрана каждого телефона разный, поэтому я понимаю, что вопросы приходят к концу экрана и переполняются, как мне прокручивать, потому что, как я уже сказал, это будет варьироваться в зависимости от экрана телефона. Я поделюсь своими кодами и скриншотом, вы лучше поймете, что я имею в виду.
@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
)
)
}
}
}
}
}
}
ЭЛЕМЕНТ
@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")
}
}
ЧатБотViewModel
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()
)
)
}
}
}
}
Есть еще 1 сообщение
(user:dsdssd)
после последнего пользовательского сообщения оно должно автоматически перемещаться туда. Если это происходит, пользователю приходится прокручивать страницу вручную, что очень неудобно.
1 ответ
Вы можете использовать LazyListState для взаимодействия с LazyList.
Учитывая функцию расширения, например
fun LazyListState.isScrolledToTheEnd() : Boolean {
val lastItem = layoutInfo.visibleItemsInfo.lastOrNull()
return lastItem == null || lastItem.size + lastItem.offset <= layoutInfo.viewportEndOffset
}
вы можете прокрутить список до конца, когда это необходимо.
var messages by remember {
mutableStateOf(emptyList<String>())
}
LaunchedEffect(true) {
while (true) {
delay(1000)
messages = messages + "Test"
}
}
val listState = rememberLazyListState()
LaunchedEffect(messages.size) {
if (!listState.isScrolledToTheEnd()) {
val itmIndex = listState.layoutInfo.totalItemsCount - 1
if (itmIndex >= 0) {
val lastItem = listState.layoutInfo.visibleItemsInfo.lastOrNull()
lastItem?.let {
listState.animateScrollToItem(itmIndex, it.size + it.offset)
}
}
}
}
LazyColumn(
state = listState
) {
...
}
Составление списков (управление положением прокрутки) документация: ссылка