автоматическая прокрутка экрана до последнего элемента джетпака

Я делаю приложение для чат-бота. Я искал во многих местах, чтобы решить эту проблему, но не мог найти ничего ясного. У меня пустой список. В этот список добавляются сообщения ботов и сообщения пользователей, но сначала он пуст. Я показываю этот список с помощью 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
) {
    ...
}

Составление списков (управление положением прокрутки) документация: ссылка

Другие вопросы по тегам