[Android/Kotlin] Infinite/endless Scrolling(무한 스크롤) 페이징

JINA·2021년 9월 1일
3
post-thumbnail

RecyclerView 무한 스크롤

무한 스크롤이란?

  • 리스트를 밑으로 스크롤 했을 때 progressBar가 보였다가,
    새로운 리스트가 추가되는 것을 말함.

📌구현방법

  1. RecyclerView 생성
  2. Scroll이 끝에 닿는 것을 감지하여 닿았을 경우 데이터에 NULL 추가 후에 Adapter에 알림
  3. 일정 시간이 지나면, NULL 제거 후 새로운 데이터 추가하고 Adapter에 알림.

👀전체코드

ContentsListActivity.kt

class ContentsListActivity : AppCompatActivity() {
    private lateinit var binding: ActivityContentsListBinding
    private var dtoList :ArrayList<UnitsDto> = ArrayList()
    private var items: ArrayList<UnitsDto?> = ArrayList()
    private lateinit var mMapLayoutManager: LinearLayoutManager
    private lateinit var mListAdapter: RecyclerViewAdapter
    private lateinit var mRecyclerView: RecyclerView
    val mainDispatcher: CoroutineDispatcher = Dispatchers.Main
    private var isLoading =false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityContentsListBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.apply {
            lifecycleOwner = this@ContentsListActivity
            activity = this@ContentsListActivity
        }

        setData()
        initAdapter()
        initScrollListener()
    }
    
    private fun setData(){
        dtoList= intent.getSerializableExtra(EXTRA_TITLE) as ArrayList<UnitsDto>
        for (i in 0 until 10){
            items.add(dtoList[i])
        }
    }
//1. RecyclerView 생성
    private fun initAdapter(){
        mListAdapter = RecyclerViewAdapter(items)
        mMapLayoutManager = LinearLayoutManager(this)
        binding.recyclerView.adapter = mListAdapter

    }
     // 2. scroll이 끝에 닿으면 데이터에 null 추가 및 Adapter에 알림
    private fun initScrollListener(){
        binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)

                if(!isLoading){
                    if ((recyclerView.layoutManager as LinearLayoutManager?)!!.findLastCompletelyVisibleItemPosition() == items.size - 1){
                        Log.e("true", "True")
                        moreItems()
                        isLoading =  true
                    }

                }
            }
        })
    }

//3. null 제거 후 새로운 데이터 추가 및 Adapter에 알림
    fun moreItems(){
        mRecyclerView = binding.recyclerView

        val runnable = Runnable {
            items.add(null)
            mListAdapter.notifyItemInserted(items.size -1)
        }
        mRecyclerView.post(runnable)

        CoroutineScope(mainDispatcher).launch {
            delay(2000)
            val runnable2=  Runnable{
                items.removeAt(items.size - 1)
                val scrollPosition = items.size
                mListAdapter.notifyItemRemoved(scrollPosition)
                var currentSize = scrollPosition
                var nextLimit = currentSize+10
                Log.e("hello", "${nextLimit}")
                if (currentSize < dtoList.size-10){
                    while (currentSize-1<nextLimit){
                        items.add(dtoList[currentSize])
                        currentSize++
                    }
                }else{
                    while (currentSize!=dtoList.size){
                        items.add(dtoList[currentSize])
                        currentSize++
                    }
                }
                mListAdapter.updateItem(items)
                isLoading = false
        }
            runnable2.run()
        }
    }
}

RecyclerViewAdapter.kt

class RecyclerViewAdapter(items: ArrayList<UnitsDto?>?) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), Filterable
{
    companion object {
        private const val TYPE_ITEM = 0
        private const val TYPE_LOADING = 1
    }
    private var context:Context? = null
    private var unFilteredList = items
    private var filteredList = items
    override fun getItemCount(): Int {
        return if (filteredList == null){
            0
        }else{
            filteredList?.size!!
        }

    }

    override fun getItemViewType(position: Int): Int {
        return when (filteredList?.get(position)) {
            null -> TYPE_LOADING
            else -> TYPE_ITEM

        }
    }
    
    fun updateItem(list:ArrayList<UnitsDto?>?){
        this.filteredList = list
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        context = parent.context
        if (viewType == TYPE_ITEM){
            val binding = RecycleritemLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
            return ItemViewHolder(binding)
        }else{
            val binding = ItemLoadingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
            return LoadingViewHolder(binding)
        }


    }
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder is ItemViewHolder){
            val item = filteredList?.get(position)
            val itemHolder = holder as ItemViewHolder
            itemHolder.bind(item!!)
        }else if (holder is LoadingViewHolder){

        }
    }
    // 아이템뷰에 프로그레스바가 들어가는 경우
    inner class LoadingViewHolder(var binding: ItemLoadingBinding) :
        RecyclerView.ViewHolder(binding.root) {

    }

    inner class ItemViewHolder(var binding: RecycleritemLayoutBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(item: UnitsDto){
            binding.nameTxtView.text = "item"
            binding.typeTxtView.text = item.type
            if (item.progress == 100){
                binding.progressTxtView.setTextColor(Color.parseColor("#B0FFFF"))
            }
            binding.progressTxtView.text = ("${item.progress}% 완료")


            Glide.with(context!!)
                .load(item.thumbnail)
                .apply(RequestOptions.bitmapTransform(RoundedCorners(15)))
                .thumbnail(0.5f)
                .into(binding.imgView)
            binding.imgView.clipToOutline = true

        }

    }
}

참고사이트1,
참고사이트2


👩‍💻TabLayout, 리사이클러뷰 필터링 구현해보기

0개의 댓글