Hướng dẫn này minh họa cách phát hiện (detect) hướng cuộn (scroll up/down) trong ListView trên Android sử dụng Kotlin.
Bước 1: Tạo dự án mới
Mở Android Studio, chọn File → New Project và điền thông tin cần thiết.
Bước 2: Thiết kế giao diện (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="8dp"
tools:context=".MainActivity">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Bước 3: Viết code MainActivity.kt
import android.os.Bundle
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private lateinit var listView: ListView
private val numbers = arrayOf(
"1", "2", "3", "4", "5", "6", "7", "8", "9", "X",
"11", "12", "13", "14", "15", "16", "17", "18", "19", "20",
"21", "22", "23", "24", "25"
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title = "KotlinApp"
val adapter = ArrayAdapter(
this,
R.layout.support_simple_spinner_dropdown_item,
numbers
)
listView = findViewById(R.id.listView)
listView.adapter = adapter
listView.setOnScrollListener(object : AbsListView.OnScrollListener {
private var lastFirstVisibleItem = 0
override fun onScrollStateChanged(view: AbsListView, scrollState: Int) {}
override fun onScroll(
view: AbsListView,
firstVisibleItem: Int,
visibleItemCount: Int,
totalItemCount: Int
) {
if (lastFirstVisibleItem < firstVisibleItem) {
Toast.makeText(
applicationContext,
"Đang cuộn xuống (Scroll Down)",
Toast.LENGTH_SHORT
).show()
} else if (lastFirstVisibleItem > firstVisibleItem) {
Toast.makeText(
applicationContext,
"Đang cuộn lên (Scroll Up)",
Toast.LENGTH_SHORT
).show()
}
lastFirstVisibleItem = firstVisibleItem
}
})
}
}
Bước 4: Cấu hình 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>
</intent-filter>
</activity>
</application>
</manifest>
Chạy ứng dụng
Kết nối thiết bị, nhấn Run. Cuộn ListView lên/xuống → Toast hiển thị "Đang cuộn lên" hoặc "Đang cuộn xuống" tương ứng.
Giải thích thuật toán
firstVisibleItem- Index của item đầu tiên hiển thị trên màn hình- So sánh
lastFirstVisibleItem(lần trước) vớifirstVisibleItem(lần hiện tại) - Tăng → Cuộn xuống (scroll down)
- Giảm → Cuộn lên (scroll up)
Cải tiến: Debounce Toast (tránh spam)
private var lastToastTime = 0L
private val TOAST_DEBOUNCE = 500L // ms
override fun onScroll(...) {
val now = System.currentTimeMillis()
if (now - lastToastTime < TOAST_DEBOUNCE) return
lastToastTime = now
// show toast...
}
RecyclerView (Hiện đại hơn ListView)
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (dy > 0) {
// Cuộn lên (content di chuyển lên)
} else if (dy < 0) {
// Cuộn xuống
}
}
})
Ứng dụng thực tế
- Ẩn/hiện FloatingActionButton khi cuộn
- Load more (pagination) khi cuộn đến cuối
- Parallax effect cho header
- Collapse/expand Toolbar