728x90
RecyclerView 키보드 스크롤 처리
- 채팅, 메신저 앱을 만들 때 흔히 사용하는 UI 구조는 헤더 - 리스트 - 푸터(EditText) 구조로 하단 EditText를 선택하면 안드로이드의 키보드가 올라오게 되는데 이 때 RecyclerView의 스크롤 처리를 하는 방법을 정리해 봤습니다.
- 스크롤 처리를 하지 않으면 키보드가 리스트를 가리게 됩니다.
- 카카오톡과 비슷하게 스크롤 처리가 가능합니다.
- 방법은 틀렸을지도 ....? ㅠㅠ
- 바쁜 분들을 위한 샘플 프로젝트
스크롤 처리
1. 키보드가 올라온 경우에만 스크롤이 가능한 경우
- 단, 이경우 키보드가 내려가면 스크롤이 불가능 함
2. 키보드가 올라온 상태에서 데이터를 추가해 키보드가 내려갔을 때에도 스크롤이 가능한 경우
3. 화면 진입 시 데이터를 불러와 처음부터 스크롤이 가능한 경우
여기서 1번과 2, 3번의 스크롤 처리 방식이 조금 다릅니다, 아래 예제에서 확인해보겠습니다.
예제
- 예제는 Kotlin + Databinding 으로만 구성되어 있습니다, 전체 코드는 샘플 프로젝트 확인 부탁드립니다!
- 테스트 UI 구조
- EditText에 데이터를 입력하고 SEND 버튼을 누르면 아이템을 추가하는 간단한 방식입니다.
1번 스크롤 처리
- 키보드가 올라온 경우에만 스크롤이 가능할 때는 RecyclerView의 OnLayoutChangeListener로 쉽게 처리가 가능합니다.
binding.rvDataList.apply {
adapter = sampleDataAdapter
addItemDecoration(SpaceDecoration())
/**
* 1. 키보드가 올라온 경우에만 스크롤이 가능한 경우 처리
* - 키보드가 내려간 경우 스크롤이 불가능하지만 키보드가 올라오면서 스크롤이 가능한 경우
* */
addOnLayoutChangeListener(onLayoutChangeListener)
}
-------------
private val onLayoutChangeListener =
View.OnLayoutChangeListener { _, _, _, _, bottom, _, _, _, oldBottom ->
// 키보드가 올라와 높이가 변함
if (bottom < oldBottom) {
binding.rvDataList.scrollBy(0, oldBottom - bottom) // 스크롤 유지를 위해 추가
}
}
- 키보드가 올라오면 RecyclerView의 높이가 변경되는데 이 때 변경된 Bottom 값이 이전 Bottom값 보다 작다면 키보드가 올라온 상태로 이전 Bottom(oldBottom) - 변경된 Bottom(bottom) 를 스크롤 Y값으로 설정해 키보드가 올라왔을 때 리스트를 가리지 않고 키보드를 올릴 수 있습니다.
- 키보드가 올라오지 않은 상태에서는 스크롤이 불가능하지만 여기서 키보드가 올라올 때는 리스트가 살짝 밀려 올라가야 합니다.
2, 3번 스크롤 처리
- 2, 3번 스크롤 처리를 위해서는 키보드의 Open 여부를 확인할 필요가 있습니다.
- 키보드의 상태 체크를 위한 코드를 추가합니다.
private fun setupView() {
// 키보드 Open/Close 체크
binding.clRootContainer.viewTreeObserver.addOnGlobalLayoutListener {
val rect = Rect()
binding.clRootContainer.getWindowVisibleDisplayFrame(rect)
val rootViewHeight = binding.clRootContainer.rootView.height
val heightDiff = rootViewHeight - rect.height()
isOpen = heightDiff > rootViewHeight * 0.25 // true == 키보드 올라감
}
}
- RecyclerView의 스크롤 상태 체크를 위한 함수를 추가해줍니다.
/**
* 세로 스크롤 가능 여부 확인
* */
fun RecyclerView.isScrollable(): Boolean {
return canScrollVertically(1) || canScrollVertically(-1)
}
- RecyclerView는 최상단 부분을 유지하려는 특성(?)이 있어서 키보드가 올라왔을 때 알아서 스크롤되지 않습니다, 그래서 레이아웃에 맞게 알아서 스크롤(밀려 올라가도록) 처리가 되도록 하기 위해 최상단을 하단으로 설정할 필요가 있습니다.
- 해당 설정은 LinearLayoutManager의 stackFromEnd 설정입니다.
- 단, stackFromEnd 설정의 경우 데이터가 없을 떄 설정하면 아이템이 하단에 붙어서 나오기 떄문에 처음부터 설정하는것은 추천드리지 않습니다. (UI에 따라 다를 수 있음)
- reverseLayout 설정도 동일하게 하단을 끝으로 인식하지만 리스트 순서가 반대로 변하니 주의하셔야 합니다.
- 해당 설정은 LinearLayoutManager의 stackFromEnd 설정입니다.
- stackFromEnd 설정을 위한 확장 함수를 추가해줍니다.
/**
* StackFromEnd 설정
* */
fun RecyclerView.setStackFromEnd() {
(layoutManager as? LinearLayoutManager)?.stackFromEnd = true
}
- 이제 스크롤의 변경 상태를 감지해 stackFromEnd를 설정하기 위해 RecyclerView에 OnScrollChangedListener 설정을 해줍니다.
binding.rvDataList.apply {
adapter = sampleDataAdapter
addItemDecoration(SpaceDecoration())
/**
* 1. 키보드가 올라온 경우에만 스크롤이 가능한 경우 처리
* 키보드가 내려간 경우 스크롤이 불가능하지만 키보드가 올라오면서 스크롤이 가능한 경우
* */
addOnLayoutChangeListener(onLayoutChangeListener)
/**
* 2. 키보드가 올라온 상태에서 데이터를 추가해 키보드가 내려갔을 때에도 스크롤이 가능한 경우
* 3. 화면 진입 시 데이터를 불러와 청므부터 스크롤이 가능한 경우
* 키보드가 열리지 않은 상태에서 스크롤 가능 상태이면 StackFromEnd 설정
* 키보드가 열린 상태에서 체크하면 키보드가 사라질 때 목록이 하단에 붙을 수 있음
* */
viewTreeObserver.addOnScrollChangedListener {
if (isScrollable() && !isOpen) { // 스크롤이 가능하면서 키보드가 닫힌 상태일 떄만
setStackFromEnd()
removeOnLayoutChangeListener(onLayoutChangeListener)
}
}
}
- stackFromEnd 설정을 해주면 위에서 설정해준 OnLayoutChangeListener는 설정할 필요가 없으니 해제해줍니다.
- 만약 OnLayoutChangeListener를 유지하면 키보드를 닫을 때 스크롤이 자동으로 최하단으로 이동하게 됩니다.
확인
- 키보드가 올라온 상태에서 데이터를 추가 (1~17) > 하단을 14번에 맞추고 키보드를 올림 (스크롤 유지됨) > 키보드를 다시 내림 (스크롤 유지됨)
- 화면 진입 시 데이터를 불러와 이미 스크롤이 가능한 상태
private fun setupData() { // 더미 데이터 설정
for (i in 1..20) dataList.add(i.toString())
sampleDataAdapter?.submitList(dataList.toList())
}
- 최초 진입 시 스크롤이 가능한 상태 > 하단을 16번으로 맞추고 키보드 올림 (스크롤 유지) > 이 상태에서 하단을 13번으로 변경 > 키보드 내림 (스크롤 유지)
* 글에 틀린 부분이 있으면 댓글 부탁드립니다 :D
728x90
'개발 > Android' 카테고리의 다른 글
[Android] AccountManager 기본 예제 (4) | 2021.09.19 |
---|---|
[Android] FragmentDirections 클래스가 생성되지 않을 때 (0) | 2021.04.09 |
[Android] MVVM + Navigation Graph 사용 시 프래그먼트 이동 처리 방법 (0) | 2021.04.09 |
[Android] BottomNavigationView 선택되지 않은 메뉴의 라벨이 보이지 않을 때 (0) | 2021.04.03 |
[Android] 클래시 오브 클랜 맵(배치) 복사 앱을 만드는 방법 (0) | 2021.01.25 |