[Android] RecyclerView에서 페이징 처리하기 #1
RecyclerView에서 리스트를 스크롤하다가 어느 순간에 로딩 화면이 뜨면서 리스트가 늘어나는 것을 본 적 있을 것이다. Adapter에서 등록된 list가 마지막에 도달했을 때를 감지하여 로딩 화면을 띄우...
ogyong.tistory.com
저번에 RecyclerView에서 문자열 배열이 저장된 파일을 이용해 페이징 처리를 해봤는데,
이번에는 실제로 Room에 저장된 데이터의 페이징과 삭제 처리를 해보려고 한다.
RecyclerView 페이징 예제
예제에서 Room, ViewModel, LiveData, Coroutine, ViewBinding을 사용한다.
1부터 30까지의 값을 Room을 이용해서 기기에 저장한다.
(데이터 저장은 별도로 구현하지 않고 App Inspection을 사용해서 저장, 아래 링크 참고)
[Android] Android Studio에서 쿼리 실행하기
Android Studio 하단 탭에 있는 App Inspection 또는 상단 탭의 View > Tool Windows > App Inspection을 클릭해서 Database Inspector을 띄운다. Database Inspectore에서 빨간색 박스로 표시해둔 아이콘을 선택하면 New Query 탭...
ogyong.tistory.com
저장된 값을 호출하여 RecyclerView로 그려주고 아이템을 삭제했을 때도 앱이 제대로 동작하는지 확인할 것이다.
참고로 파일 구조는 아래와 같다.
![[Android] RecyclerView에서 페이징+삭제 처리하기 (with Room) #2 - RecyclerView 페이징 예제 [Android] RecyclerView에서 페이징+삭제 처리하기 (with Room) #2 - RecyclerView 페이징 예제](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
◾ 의존성 추가
plugins {
id 'kotlin-kapt'
}
dependencies {
// Room 의존성 추가
implementation "androidx.room:room-runtime:2.4.3"
kapt "androidx.room:room-compiler:2.4.3"
// ViewModel 의존성 추가
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
}
build.gradle 파일에 위 내용을 추가하고 sync를 해준다.
◾ Room 구성 요소 세팅 (data 패키지에 있는 파일들)
▫ SampleData
@Entity(tableName = "sample")
data class SampleData(
@ColumnInfo(name = "title") val title: String
) {
@PrimaryKey(autoGenerate = true)
var id: Int = 0
}
@Entity를 사용해서 데이터베이스의 테이블 명을 작성하고 테이블의 Row 값을 설정해준다.
RecyclerView에 표시할 1~30의 텍스트가 title 값이다.
▫ SampleDao
@Dao
interface SampleDao {
/**
* 리스트 사이즈 호출
*/
@Query("SELECT COUNT(*) FROM sample")
fun getListSize(): Int
/**
* 아이템 10개씩 호출
*/
@Query("SELECT * FROM sample ORDER BY id ASC LIMIT 10 OFFSET (:page-1)*10")
fun getList(page:Int): List<SampleData>
/**
* 아이템 삭제
*/
@Query("DELETE FROM sample WHERE id = :id")
fun itemDelete(id: Int)
}
sample 테이블과 상호작용 할 수 있도록 Dao를 정의한다.
리스트 사이즈 호출은 마지막 페이지가 어디인지 확인하는 용도로 추가했다.
▫ SampleDatabase
@Database(entities = [SampleData::class], version = 1)
abstract class SampleDatabase: RoomDatabase() {
abstract fun getSampleDao() : SampleDao
}
SampleData를 엔티티로 하는 데이터베이스 클래스를 정의해준다.
◾ 현재 페이지가 마지막 페이지인지 알아내기
RecyclerView의 addOnScrollListener를 통해서 현재 리스트의 마지막 아이템이 감지되면 다음 페이지를 호출할 수 있다.
하지만 아이템이 없는 경우와 마지막 페이지일 경우에는 다음 페이지를 호출하면 안 된다.
리스트에 값이 없거나 현재 페이지가 마지막 페이지인지 알기 위해서
SampleDao에서 리스트의 사이즈를 반환하는 메서드(getListSize)를 작성했다.
getListSize로 구한 데이터베이스에 저장된 아이템의 개수와 RecyclerView에 표시되는 아이템의 개수를 비교하여
현재 페이지가 마지막 페이지인지 구한다.
// MainActivity의 onCreate
mSampleDB = Room.databaseBuilder(
applicationContext, SampleDatabase::class.java, "sample_database"
).build()
mViewModel.getSampleListSize(mSampleDB) // DB에 저장된 리스트의 사이즈 호출
MainActivity의 onCreate에서 데이터베이스 객체를 생성하고 MainViewModel의 getSampleListSize을 호출한다.
// MainViewModel
val sampleListSizeObserve: MutableLiveData<Int> = MutableLiveData()
fun getSampleListSize(db: SampleDatabase) {
viewModelScope.launch(Dispatchers.IO) {
sampleListSizeObserve.postValue(db.getSampleDao().getListSize())
}
}
// MainActivity
mViewModel.sampleListSizeObserve.observe(this) {
println("리스트 사이즈 호출 결과 : $it")
if(it == 0 || it == null) {
return@observe
}
getIsLastPage(it) // 마지막 페이지인지 판별
mViewModel.getSampleList(mSampleDB, page)
}
MainViewModel에서 리스트의 사이즈 값을 LiveData로 받아서 MainActivitiy에서 observe한다.
observe에서 리스트 사이즈 값이 있는 경우 마지막 페이지인지 판별하는 메서드(getIsLastPage)를 호출하고
MainViewModel의 getSampleList를 호출해서 리스트 정보를 구한다.
// MainActivity
private fun getIsLastPage(dbSize: Int) {
val rvPageSplit = (mAdapter.itemCount / 10.0f).toString().split(".")
val dbPageSplit = (dbSize / 10.0f).toString().split(".")
val rvPage = if(rvPageSplit[1] == "0") {
rvPageSplit[0].toInt()
}else {
(rvPageSplit[0].toInt())+1
}
val dbPage = if(dbPageSplit[1] == "0") {
dbPageSplit[0].toInt()
}else {
(dbPageSplit[0].toInt())+1
}
isLastPage = rvPage == dbPage // 필드 변수
}
Ex) 21개의 아이템 이 있고, 10개씩 3페이지로 나눈다고 가정.
RecyclerView에는 20번째 아이템 표시되고 있음 → 현재 2page
데이터베이스에는 21개의 아이템이 있음 → 총 3page
각 값을 10으로 나누면 각각 2.0, 2.1이 된다.
소수 부분이 0일 때는 정수 부분이 페이지 값이고
소수 부분이 0이 아니면 정수 부분에 1을 더한 값이 페이지 값이 된다.
◾ 페이징 처리하기
// MainActivity
mBinding.rvMain.addOnScrollListener(object: RecyclerView.OnScrollListener(){
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
// 리사이클러뷰 아이템 위치 찾기, 아이템 위치가 완전히 보일때 호출됨
val rvPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!.findLastCompletelyVisibleItemPosition()
// 리사이클러뷰 아이템 총 개수 (index 접근 이기 때문에 -1)
val totalCount = recyclerView.adapter?.itemCount?.minus(1)
// 페이징 처리
if(rvPosition==totalCount && !isLastPage && totalCount>=0) {
page++
mViewModel.getSampleListSize(mSampleDB)
}
}
})
RecyclerView에 있는 마지막 아이템이 보일 때 마지막 페이지가 아니고 빈 리스트가 아니면
page(현재 페이지) 값을 증가 시키고 다음 페이지 정보(getSampleListSize)를 호출한다.
![[Android] RecyclerView에서 페이징+삭제 처리하기 (with Room) #2 - RecyclerView 페이징 예제 - ◾ 페이징 처리하기 [Android] RecyclerView에서 페이징+삭제 처리하기 (with Room) #2 - RecyclerView 페이징 예제 - ◾ 페이징 처리하기](https://blog.kakaocdn.net/dn/qVOtV/btrVA1cyDMb/oJNGrIRYaQCIUaKmftE1t0/img.gif)
◾ 아이템 삭제
// MainActivity
// x버튼 클릭
mAdapter.removeListener(object: CustomListenerInterface{
override fun removeListener(position: Int, sampleData: SampleData) {
mViewModel.setItemDelete(mSampleDB, sampleData.id)
deleteItem = sampleData
}
})
mViewModel.itemDeleteObserve.observe(this) {
println("아이템 삭제 호출")
isDeleteCall = true
mAdapter.removeItem(deleteItem!!)
if(!isLastPage) {
mViewModel.getSampleListSize(mSampleDB)
}
}
mViewModel.sampleListObserve.observe(this) {
println("리스트 호출 결과 : $it")
if(it.isNullOrEmpty()) {
return@observe
}
if(isDeleteCall && !isLastPage) {
isDeleteCall = false
mAdapter.addLastData(it.last())
}else {
mAdapter.setList(it)
}
}
x버튼을 클릭했을 때 삭제된 아이템을 setItemDelete를 호출해서 데이터베이스에 적용되도록 하고
현재 페이지의 리스트를 호출하여 마지막 아이템을 추가해야 한다.(아래 링크 참고)
[Android] 리스트에서 아이템 삭제 후 페이징할 때 조심할 점
RecyclerView의 addOnScrollListener를 사용해서 리스트의 마지막 위치가 표시될 때 다음 페이지에 대한 정보를 서버에 요청하여 페이징 작업을 하였다. 문제는 리스트에서 아이템을 삭제하고 다음 페이...
ogyong.tistory.com
![[Android] RecyclerView에서 페이징+삭제 처리하기 (with Room) #2 - RecyclerView 페이징 예제 - ◾ 아이템 삭제 [Android] RecyclerView에서 페이징+삭제 처리하기 (with Room) #2 - RecyclerView 페이징 예제 - ◾ 아이템 삭제](https://blog.kakaocdn.net/dn/KGkyW/btrVO0JCnlQ/OGkK7iOtK7wVoBzSzTaL4k/img.gif)
'Android > RecyclerView' 카테고리의 다른 글
[Android] RecyclerView에서 페이징 처리하기 #1 (0) | 2022.12.16 |
---|---|
[Android] Expandable RecyclerView 구현하기 (0) | 2022.11.22 |
[Android] RecyclerView Drag and Drop 구현하기 (0) | 2022.11.18 |
[Android] RecyclerView 클릭 이벤트 처리하기 (0) | 2022.11.02 |
댓글