ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Android] 무한스크롤 Infinite Scroll
    AOS 2023. 5. 17. 02:26
    반응형

    무한스크롤이란?

    API 데이터가 처음부터 모드 로드되지 않고, 사용자가 요청했을 때 다음 데이터가 로드되는 방법으로는 무한스크롤과 pagination(페이지 매기기)가 있습니다.

    그 중 무한스크롤은 사용자가 스크롤 하면서 페이지의 하단에 도달했을 때, 다음 데이터가 로드되는 되는 UX 입니다. 

    pagination은 페이지를 분할하여  사용자가 페이지를 클릭해야 다음 데이터가 로드됩니다.

    스크롤은 클릭보다 상호작용 비용이 낮아 더 나은 UX를 제공합니다.

     

    무한스크롤을 구현해보기

    1. 데이터 불러오기 (ViewModel)

    class PlantListViewModel(private val repository: PlantRepository) : ViewModel() {
    
        private val _plantList = MutableLiveData<PlantList>()
        var plantList : LiveData<PlantList> = _plantList
    
        // 처음 list를 불러옵니다.
        fun initList() {
            repository.list { plantList ->
                _plantList.postValue(plantList)
            }
        }
    
        // 다음 list를 불러옵니다.
        fun nextList() {
            val currentPlantList = plantList.value ?: return
            repository.next(currentPlantList) { palntList ->
                // 기존 list와 다음 list를 더해줍니다.
                val mergedPlants = currenPlantList.plants.toMutableList()
                    .apply { addAll(plantList.plants) }
                plantList.plants = mergedPlants
                _plantList.postValue(plantList)
            }
        }
    }

    Next List를 불러올 때는 기존의 list와 합쳐서 다시 저장합니다.

     

    2. 스크롤 감지 

    binding.plantList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)
            
            // 다음페이지 존재 여부 확인
            if(viewModel.hasNextPage() == false) return;
    
            // 아직 로딩중인지 확인
            if (viewModel.progressVisible.value != true) {
                // 스크롤이 끝에 도달했는지 확인
                if (!binding.plantList.canScrollVertically(1)) {
                    binding.progressBar.isVisible = true
                    viewModel.nextList()
                }
            }
        }
    })

    스크롤이 끝에 도달했으면 viewModel.next()를 호출하여 다음 list 데이터를 불러옵니다.

     

    주의해야할 점

    • 다음 페이지가 없으면 (viewModel 에서 hasNextPage 활용)
    • 아직 로딩중이라면 (progressBar 활용)

    요청되지 않아야 합니다.

     

    +) 다른 방법 : 마지막으로 보여진 아이템 position 이 전체 아이템 개수보다 5개 모자란 경우 데이터를 불러오자.

    binding.plantList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
          override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
              super.onScrolled(recyclerView, dx, dy)
              
              // 다음페이지 존재 여부 확인
              if(viewModel.hasNextPage() == false) return;
    
              val layoutManager = binding.plantList.layoutManager
              
              val lastVisibleItem = (layoutManager as LinearLayoutManager)
                  .findLastCompletelyVisibleItemPosition()
    
              // 아직 로딩중인지 확인
              if (viewModel.progressVisible.value != true) {
                  // 마지막으로 보여진 아이템 position 이 전체 아이템 개수보다 5개 모자란 경우
                  if (layoutManager.itemCount <= lastVisibleItem + 5) {
                      binding.progressBar.isVisible = true
                      viewModel.nextList()
                  }
              }
          }
    })

    마지막으로 보여진 아이템의 position을 이용하여

    전체 아이템의 갯수보다 5개 부족한경우 데이터를 load 합니다.

    3. list 감지 > adapter 업데이트

    viewModel.plantList.observe(this){
            binding.pullToRefresh.isRefreshing = false
            binding.progressBar.isVisible = false
            adapter.updatePlants(it.plants)
    }
    
    binding.pullToRefresh.setOnRefreshListener {
        viewModel.initList()
    }
    
    viewModel.initList()
    • 처음 화면에 들어왔을 때 ( viewModel.initList() )
    • pull to refresh를 했을 때 ( viewModel.initList() )
    • 스크롤을 하단으로 내릴 때 ( viewModel.nextList() )

    위의 세가지 경우에 list가 업데이트 됩니다.

    4. PlantsAdapter.kt 의 update 함수

    private val plants = mutableListOf<Plant>()
    
    fun updatePlants(plants: List<Plant>) {
        this.plants.clear()
        this.plants.addAll(plants)
        notifyDataSetChanged()
    }

    adapter에서 list를 update 해주는 함수입니다.

    반응형
Designed by Tistory.