현재 프로젝트에서 댓글창에서 사용하는 RecyclerView 의 Adapter 로 ListAdapter 를 사용하고 있다.
뷰모델에서 서버로부터 댓글들을 받아와서 ListAdapter 의 submitList 함수를 이용하여 리사이클러뷰에 새로운 아이템을 반영해준다.
그리고 만약 새 댓글이 정상적으로 전송이 되면 smoothScrollToPosition(0)
함수를 호출하여 리사이클러뷰의 맨 위로 스크롤이 되게 했다.
그러나 위의 코드를 실제로 실행해보면 최상단으로 스크롤이 되지 않는다.
뭐가 문제일까?
일단 ListAdapter 가 뭔지 그 작동 방식을 살펴보자.
내용이 너무 많아져서 따로 글을 썼다. 읽고 오자!
https://velog.io/@woonyumnyum/ListAdapter-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0
핑구의 글도 읽고 오자!
https://velog.io/@pingu244/DiffUtil%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%83%90
이제 어떤 문제인지 감이 온다.
ListAdapter 의 submitList 함수는 내부적으로 background thread 에서 비동기적으로 동작하기 때문에, 새 아이템이 반영되기 전 smoothScrollToPosition 함수가 실행된 것이다.
따라서 새 아이템이 반영된 후 최상단으로 스크롤 되도록 구현해야 한다.
그러면 새로운 List 의 반영이 완료되면 callback 함수가 실행될 것이다.
recyclerView.apply {
val commentAdapter: CommentAdapter = adapter as? CommentAdapter ?: return
commentAdapter.submitList(
succeedCommentResponseUiState.comments
) { smoothScrollToPosition(0) }
}
View.post 메소드를 살펴보자
View.post 메소드의 정의는 다음과 같다.
public boolean post(Runnable action)
함수가 어떻게 동작하는지 구현부를 살펴보자
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
Handler.post 함수는 실행 가능 Runnable 을 메시지 큐에 추가한다. 이때 핸들러는 ui thread 에 연결되어 있기 때문에 Runnable 은 ui thread 에서 실행된다.
뷰가 Attach 된 후 Runnable이 성공적으로 배치되었다는 가정 하에 Run Queue 를 얻어서 Runnable 에 대해 Queue의 post 함수를 실행한다.
이때getRunQueue()
로 얻어지는 객체는 핸들러가 연결되지 않은 경우 뷰에서 보류 중인 작업을 큐에 대기열에 추가하는 데 사용되는 클래스이다.
AttachInfo: 뷰가 parent window 에 연결될 때 뷰에 제공되는 정보 집합
해결하기
post 메소드로 넘겨진 람다 smoothScrollToPosition
메소드가 메시지큐에 추가된다.
그리고 해당 메시지는 submitList 가 완료된 후 실행될 것이다!
그럼 List 가 반영된 후 최상단으로 스크롤 하게 되니까 정상적으로 실행된다!
recyclerView.apply {
val commentAdapter: CommentAdapter = adapter as? CommentAdapter ?: return
commentAdapter.submitList(
succeedCommentResponseUiState.comments
)
post { smoothScrollToPosition(0) }
}
결론
submitList 에 콜백을 넘겨서 실행시키는 방법과 View.post 함수를 이용하는 방법 둘다 Message Queue 에 메시지를 넣고 Handler 의 post 함수를 사용하는 방법이다.
댓글 전송에 성공하면 리사이클러뷰의 최상단으로 자동 스크롤이 잘 되는 것을 볼 수 있다!
참고