최근에 시작한 프로젝트를 진행하다가 LazyColumn 을 사용해서 실시간 채팅 기능을 구현했는데, 가장 최근에 받은 메시지 item의 위치로 Scroll 하는 방법에 대해서 삽질을 하다가 알아낸 방법입니다.
@Composable
fun ChatList(
roomId: String,
currentUser: User,
viewModel: MainViewModel
){
val chatList by viewModel.getRoomMessage(roomId).observeAsState()
var previousChat by remember { mutableStateOf(listOf<Chat>()) }
val listState = rememberLazyListState()
val isKeyboardOpen by keyboardAsState()
val coroutineScope = rememberCoroutineScope()
Log.d("testt", "isKeyboardOpen: ${isKeyboardOpen}")
chatList?.let {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(10.dp),
state = listState,
){
items(
items = chatList!!,
key = { it.chat_key }
){ chat->
if(chat.userInfo.uid == currentUser.uid){
// 나의 채팅
MyChatItem(chat = chat)
}else{
// 다른 사람의 채팅
OtherChatItem(chat = chat)
}
}
if(isKeyboardOpen == Keyboard.Opened || previousChat.size != it.size) {
coroutineScope.launch {
listState.scrollToItem(it.size - 1)
}
}
}
previousChat = it
}
}
chatList(모든 채팅 메시지)를 observeAsState() 로 관리하기 때문에 어느시점에 새로운 메시지가 왔는지 알아야 합니다.
var previousChat by remember { mutableStateOf(listOf<Chat>()) }
그래서 새로운 메시지가 오기 이전의 메시지들을 저장할 State 를 따로 선언해놓습니다.
if(isKeyboardOpen == Keyboard.Opened || previousChat.size != it.size) {
coroutineScope.launch {
listState.scrollToItem(it.size - 1)
}
}
키보드가 올라오거나, 이전 메시지 개수와 현재 메시지 개수가 일치하지 않으면 현재 리스트의 마지막 아이템 인덱스로 스크롤합니다.
enum class Keyboard {
Opened, Closed
}
@Composable
fun keyboardAsState(): State<Keyboard> {
val keyboardState = remember { mutableStateOf(Keyboard.Closed) }
val view = LocalView.current
DisposableEffect(view) {
val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener {
val rect = Rect()
view.getWindowVisibleDisplayFrame(rect)
val screenHeight = view.rootView.height
val keypadHeight = screenHeight - rect.bottom
keyboardState.value = if (keypadHeight > screenHeight * 0.15) {
Keyboard.Opened
} else {
Keyboard.Closed
}
}
view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener)
onDispose {
view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener)
}
}
return keyboardState
}
현재 키보드의 상태를 리턴하는 메서드입니다.
좋은 글 감사합니다~