Giới thiệu
Bài viết này hướng dẫn cách triển khai danh sách vô hạn (endless/infinite scrolling) với RecyclerView trong Android sử dụng Kotlin.
Các bước thực hiện
Bước 1: Tạo dự án mới
Mở Android Studio, chọn File → New Project và điền đầy đủ thông tin để tạo dự án mới.
Bước 2: Thiết kế item layout (res/layout/item_row.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="2dp"
app:cardUseCompatPadding="true">
<TextView
android:id="@+id/textViewItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:textColor="@android:color/holo_blue_bright"
android:textStyle="bold" />
</androidx.cardview.widget.CardView>
Bước 3: Tạo loading layout (res/layout/item_loading.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:indeterminate="true"
android:paddingLeft="8dp"
android:paddingRight="8dp" />
</LinearLayout>
Bước 4: Viết RecyclerViewAdapter (RecyclerViewAdapter.kt)
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.TextView
import androidx.annotation.NonNull
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
class RecyclerViewAdapter internal constructor(private val itemList: List<String>) : RecyclerView.Adapter<ViewHolder>() {
private val viewItemType = 0
@NonNull
override fun onCreateViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder {
return if (viewType == viewItemType) {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_row, parent, false)
ItemViewHolder(view)
} else {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_loading, parent, false)
LoadingViewHolder(view)
}
}
override fun onBindViewHolder(@NonNull viewHolder: ViewHolder, position: Int) {
if (viewHolder is ItemViewHolder) {
populateItemRows(viewHolder, position)
} else if (viewHolder is LoadingViewHolder) {
showLoadingView(viewHolder, position)
}
}
override fun getItemViewType(position: Int): Int {
return viewItemType
}
private inner class ItemViewHolder internal constructor(@NonNull itemView: View) : ViewHolder(itemView) {
internal var tvItem: TextView = itemView.findViewById(R.id.textViewItem)
}
private class LoadingViewHolder internal constructor(itemView: View) : ViewHolder(itemView) {
var progressBar: ProgressBar = itemView.findViewById(R.id.progressBar)
}
override fun getItemCount(): Int {
return itemList.size
}
private fun showLoadingView(viewHolder: LoadingViewHolder, position: Int) {}
private fun populateItemRows(viewHolder: ItemViewHolder, position: Int) {
val item = itemList[position]
viewHolder.tvItem.text = item
}
}
Bước 5: Viết MainActivity (src/MainActivity.kt)
import android.os.Bundle
import android.os.Handler
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
lateinit var recyclerView: RecyclerView
lateinit var recyclerViewAdapter: RecyclerViewAdapter
var rowsArrayList: ArrayList<String> = ArrayList()
var isLoading = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title = "KotlinApp"
recyclerView = findViewById(R.id.recyclerView)
populateData()
initAdapter()
initScrollListener()
}
private fun initScrollListener() {
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val linearLayoutManager = recyclerView.layoutManager as LinearLayoutManager?
if (!isLoading) {
if (linearLayoutManager != null && linearLayoutManager.findLastCompletelyVisibleItemPosition() == rowsArrayList.size - 1) {
loadMore()
isLoading = true
}
}
}
})
}
private fun initAdapter() {
recyclerViewAdapter = RecyclerViewAdapter(rowsArrayList)
recyclerView.layoutManager = LinearLayoutManager(applicationContext)
recyclerView.adapter = recyclerViewAdapter
}
private fun populateData() {
for (i in 0..9) {
rowsArrayList.add("Number $i")
}
}
private fun loadMore() {
rowsArrayList.add("") // placeholder loading
recyclerViewAdapter.notifyItemInserted(rowsArrayList.size - 1)
val handler = Handler()
handler.postDelayed(Runnable {
rowsArrayList.removeAt(rowsArrayList.size - 1)
val scrollPosition = rowsArrayList.size
recyclerViewAdapter.notifyItemRemoved(scrollPosition)
var currentSize = scrollPosition
val nextLimit = currentSize + 10
while (currentSize - 1 < nextLimit) {
rowsArrayList.add("Number $currentSize")
currentSize++
}
recyclerViewAdapter.notifyDataSetChanged()
isLoading = false
}, 2000)
}
}
Bước 6: Cấu hình Manifest (androidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android" package="com.example.q11">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Chạy ứng dụng
Kết nối thiết bị Android thực với máy tính. Trong Android Studio, mở file activity và nhấn biểu tượng Run trên thanh công cụ. Chọn thiết bị di động của bạn và kiểm tra màn hình hiển thị.
Cải tiến hiện đại: Thay vì Handler, nên dùngCoroutine+FlowhoặcPaging 3 Library(androidx.paging:paging-runtime) cho pagination chuẩn, hỗ trợ load state, retry, refresh.