Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีตรวจจับ Scroll Up/Down ใน ListView Android

ภาพรวม

บทความนี้สาธิตวิธีตรวจจับทิศทางการเลื่อน (Scroll Up/Down) ใน ListView โดยใช้ AbsListView.OnScrollListener ซึ่งเป็นเทคนิคสำคัญสำหรับ Infinite Scroll, Hide/Show Toolbar, หรือ Load More แบบ Lazy Loading

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

สร้างโปรเจกต์ Android ใหม่ใน Android Studio

ขั้นตอนที่ 2: ออกแบบ Layout (res/layout/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:orientation="vertical"
    android:gravity="center"
    android:padding="8dp"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

ขั้นตอนที่ 3: เขียนโค้ด MainActivity (src/MainActivity.java) - เวอร์ชันปรับปรุง

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ListView listView;
    private int lastFirstVisibleItem = 0;
    private boolean isScrollingUp = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listView = findViewById(R.id.listView);

        // สร้างข้อมูลตัวอย่าง 50 รายการ
        List<String> numbers = Arrays.asList(
            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
            "11", "12", "13", "14", "15", "16", "17", "18", "19", "20",
            "21", "22", "23", "24", "25", "26", "27", "28", "29", "30",
            "31", "32", "33", "34", "35", "36", "37", "38", "39", "40",
            "41", "42", "43", "44", "45", "46", "47", "48", "49", "50"
        );

        ArrayAdapter<String> adapter = new ArrayAdapter<>(
            this, 
            android.R.layout.simple_list_item_1, 
            numbers
        );
        listView.setAdapter(adapter);

        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                // scrollState: SCROLL_STATE_IDLE, SCROLL_STATE_TOUCH_SCROLL, SCROLL_STATE_FLING
                if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                    // หยุดเลื่อนแล้ว - ทำงาน Load More ได้ที่นี่
                    checkLoadMore(view);
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                // ตรวจจับทิศทาง Scroll
                if (firstVisibleItem > lastFirstVisibleItem) {
                    // Scroll Down (ขึ้นบน -> ลงล่าง)
                    if (!isScrollingUp) {
                        isScrollingUp = false; // กำลัง Scroll Down
                        onScrollDown();
                    }
                } else if (firstVisibleItem < lastFirstVisibleItem) {
                    // Scroll Up (ลงล่าง -> ขึ้นบน)
                    if (isScrollingUp) {
                        isScrollingUp = true; // กำลัง Scroll Up
                        onScrollUp();
                    }
                }
                lastFirstVisibleItem = firstVisibleItem;
            }
        });
    }

    private void onScrollDown() {
        // Toast.makeText(this, "Scrolling Down ⬇️", Toast.LENGTH_SHORT).show();
        // ซ่อน FAB/Toolbar, Load More, etc.
    }

    private void onScrollUp() {
        // Toast.makeText(this, "Scrolling Up ⬆️", Toast.LENGTH_SHORT).show();
        // แสดง FAB/Toolbar, Refresh, etc.
    }

    private void checkLoadMore(AbsListView view) {
        // Infinite Scroll / Load More Logic
        int lastItem = view.getLastVisiblePosition();
        int totalItems = view.getCount();
        
        if (lastItem >= totalItems - 5) { // เหลือ 5 รายการ
            // loadMoreData();
        }
    }
}

ขั้นตอนที่ 4: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.com.sample">

    <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>

ย้ายไป RecyclerView (แนะนำสำหรับโปรเจกต์ใหม่)

ListView เป็น Legacy Widget แนะนำให้ใช้ RecyclerView พร้อม OnScrollListener แทน:

RecyclerView Scroll Detection (Kotlin)

recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)
        
        if (dy > 0) {
            // Scroll Down
            onScrollDown()
        } else if (dy < 0) {
            // Scroll Up
            onScrollUp()
        }
    }

    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            // หยุดเลื่อน - ตรวจสอบ Load More
            val layoutManager = recyclerView.layoutManager as LinearLayoutManager
            val lastVisible = layoutManager.findLastVisibleItemPosition()
            val totalItem = layoutManager.itemCount
            if (lastVisible >= totalItem - 5) {
                // loadMore()
            }
        }
    }
})

กรณีการใช้งานจริง (Use Cases)

  • Hide/Show Toolbar/FAB: ซ่อนเมื่อ Scroll Down, แสดงเมื่อ Scroll Up
  • Infinite Scroll / Pagination: โหลดข้อมูลเพิ่มเมื่อเลื่อนใกล้ล่าง
  • Pull to Refresh: ตรวจจับ Scroll Up ที่ตำแหน่ง 0
  • Analytics: วัดการมีส่วนร่วมของผู้ใช้ (Engagement)
  • Image Lazy Loading: โหลดรูปเฉพาะ Item ที่มองเห็น

ข้อควรระวัง

  • onScroll เรียกบ่อยมาก - หลีกเลี่ยงการทำ Heavy Operation ภายใน
  • ใช้ Debounce/Throttle หากต้อง Trigger Action บ่อย
  • ListView deprecated ใน API 26+ ให้ย้ายไป RecyclerView
  • สำหรับ NestedScrollView ใช้ OnScrollChangeListener แทน