관리 메뉴

나만을 위한 블로그

[Android] 리사이클러뷰 페이징 처리 (페이징 라이브러리 X) 본문

Android

[Android] 리사이클러뷰 페이징 처리 (페이징 라이브러리 X)

참깨빵위에참깨빵_ 2023. 1. 30. 02:01
728x90
반응형

페이징 라이브러리를 사용하지 않고 리사이클러뷰만으로도 충분히 페이징 처리가 가능하다.

대신 페이징 처리하려면 내가 꼭 알고 있어야 하는 값 2가지가 있다. 백엔드 개발자가 API 안에 넣어서 줄 것이다.

 

  • 한 번에 몇 개의 데이터를 불러올 건지
  • 몇 번째 페이지를 요청할 건지

 

편의상 한 번에 불러올 데이터의 양은 limit, 서버에 요청하는 페이지는 page로 쓴다.

 

형태는 조금씩 차이가 있겠지만 API 응답값을 확인하면 대충 아래와 비슷한 형태일 것이다.

 

{
    "page": 1,
    "limit": 20,
    "totalCount": 1342,
    "data": ...
}

 

클라이언트가 몇 번째 페이지를 요청했는데, 한 번에 가져갈 데이터는 몇 개고, 총 데이터는 몇 개인지, 요청한 페이지의 데이터는 어떤 것이 있는지 등을 보내줄 것이다.

리사이클러뷰를 사용한 페이징 처리는 addOnScrollListener()라는 함수를 통해 구현할 수 있다. 예를 들어 아래 코드처럼 작성할 수 있다.

 

myRecyclerView.apply {
    addOnScrollListener(object : RecyclerView.OnScrollListener() {
        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)
            val lastVisibleItemPosition =
                (recyclerView.layoutManager as LinearLayoutManager?)!!.findLastCompletelyVisibleItemPosition()
            val itemTotalCount = recyclerView.adapter!!.itemCount
            if (lastVisibleItemPosition >= itemTotalCount - 1) {
                if (page * 10 < totalItemCount) {
                    page++
                    requestDTO.pages = page
                    myFunction(requestDTO)
                }
            }
        }
    })
    .
    .
    .
}

 

하나씩 확인해 본다.

 

  • lastVisibleItemPosition : 현재 리사이클러뷰에서 가장 마지막(맨 밑)에 표시되는 아이템의 position이다. 이 위치를 기준으로 page 변수값에 +1을 해서 새 데이터를 가져올 것이다
  • itemTotalCount : 어댑터로 넘어간 리스트의 크기다. 만약 limit가 50개라면 여기서 50을 리턴할 것이고, 페이징 데이터를 한 번 추가로 받아왔다면 100이 표시될 것이다. 이 값에서 1을 빼서 마지막 아이템의 이전 아이템 position을 보게 한다
  • 맨 밑에 위치한 아이템의 position이 itemTotalCount - 1보다 크거나 같을 경우, page 변수값에 10을 곱한 값이 총 아이템 개수보다 작은 경우 page 변수값을 +1해서 서버에 요청한다

 

이 중 findLastCompletelyVisibleItemPosition()의 역할은 아래와 같다.

 

https://developer.android.com/reference/kotlin/androidx/recyclerview/widget/LinearLayoutManager#findLastCompletelyVisibleItemPosition() 

 

LinearLayoutManager  |  Android Developers

androidx.car.app.managers

developer.android.com

마지막으로 완전히 보이는 뷰의 어댑터 위치를 반환한다. 이 위치에는 마지막 레이아웃 패스 이후에 전달된 어댑터 변경사항이 포함되지 않는다. 경계 검사는 현재 방향에서만 수행된다. 즉 레이아웃 매니저가 수평인 경우 뷰의 왼쪽 및 오른쪽 가장자리만 확인한다

 

그리고 어댑터에 데이터를 넘긴다. totalItemCount는 var 전역변수로 선언하고 0으로 초기화하고, 서버에서 데이터를 땡겨왔을 때 총 데이터 개수를 totalItemCount에 담아둔다.

아래는 예시 코드다.

 

viewModel.apply {
    myFunction(requestDTO)
    myState.collect {
        when (it) {
            is State.Success -> {
                it.data?.let { data ->
                    val result = data.result
                    totalItemCount = data.totalCount
                    if (result.isNotEmpty()) {
                        list.addAll(result)
                        initAdapter(list)
                    }
                }
                binding.isLoading = false
            }
            is State.Error -> {
                it.message?.let { message ->
                    showMessage(message)
                }
                binding.isLoading = false
            }
            is State.Loading -> { binding.isLoading = true }
        }
    }
}

private fun initAdapter(list: ArrayList<RequestDTO>) {
    recyclerViewState = binding.myRecyclerView.layoutManager?.onSaveInstanceState()!!
    myAdapter = MyAdapter(requireActivity(), list)
    binding.myRecyclerView.apply {
        setHasFixedSize(true)
        adapter = myAdapter
        layoutManager?.onRestoreInstanceState(recyclerViewState)
    }
}

 

initAdapter() 안의 recyclerViewState는 전역변수로 선언한 lateinit var Parcelable 프로퍼티인데, 페이징으로 새 데이터를 가져오면 리사이클러뷰 마지막에 있던 스크롤이 갑자기 맨 위로 이동해버린다. 이것을 막고 마지막 스크롤했던 위치를 기억해뒀다가, 새 데이터들을 가져오면 첫번째 새 데이터부터 스크롤할 수 있게 해주는 장치다.

자세한 내용은 아래 포스팅을 참고하면 된다.

 

https://onlyfor-me-blog.tistory.com/361

 

[Android] 리사이클러뷰 스크롤 중 데이터가 변경될 때 현재 위치를 유지하는 방법

페이징 또는 TimerTask 등을 통해 메서드를 반복호출한 다음, 메서드 리턴값을 리사이클러뷰에 보여주고 notifyDataSetChanged()를 호출할 경우 스크롤이 맨 위로 올라갈 수 있다. 이번 포스팅에선 페이

onlyfor-me-blog.tistory.com

 

이렇게 하면 페이징 처리가 되서 바닥까지 스크롤하면 새 데이터를 가져와서 화면에 보여줄 것이다.

반응형
Comments