Computer >> 컴퓨터 >  >> 프로그래밍 >> Android

Android ListView에서 스크롤 방향(위·아래) 감지하는 방법 완벽 가이드

안드로이드 앱을 개발하다 보면 사용자가 리스트를 어느 방향으로 스크롤하는지 감지해야 하는 경우가 자주 있습니다. 예를 들어 아래로 스크롤할 때는 툴바를 숨기고, 위로 스크롤할 때는 다시 보여주는 식의 UI 동작에 활용할 수 있습니다.

이 글에서는 AbsListView.OnScrollListener를 활용해 Android ListView에서 위로 스크롤과 아래로 스크롤을 감지하는 방법을 단계별로 살펴보겠습니다.

핵심 원리

스크롤 방향 감지의 핵심은 onScroll() 콜백에서 전달되는 firstVisibleItem 값을 추적하는 것입니다. 현재 화면에 보이는 첫 번째 아이템의 위치가 이전 값보다 커지면 아래로 스크롤 중인 것이고, 작아지면 위로 스크롤 중인 것입니다.

Step 1 — 새 프로젝트 생성

Android Studio에서 File ⇒ New Project를 선택하고, 필요한 정보를 모두 입력해 새 프로젝트를 생성합니다.

Step 2 — 레이아웃 작성 (res/layout/activity_main.xml)

아래 코드를 activity_main.xml에 추가합니다. 화면 중앙에 ListView 하나를 배치하는 간단한 구조입니다.

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

Step 3 — MainActivity 구현 (src/MainActivity.java)

이제 핵심 로직을 작성합니다. setOnScrollListener()로 리스너를 등록하고, onScroll() 안에서 이전 firstVisibleItem 값과 현재 값을 비교해 스크롤 방향을 판별합니다.

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;

public class MainActivity extends AppCompatActivity {

    ListView listView;
    String[] numbers = {"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"};

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

        ArrayAdapter adapter = new ArrayAdapter<>(this,
                R.layout.support_simple_spinner_dropdown_item, numbers);

        listView = findViewById(R.id.listView);
        listView.setAdapter(adapter);

        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            private int lastFirstVisibleItem;

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                // 스크롤 상태 변화가 필요하다면 여기서 처리
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem,
                                 int visibleItemCount, int totalItemCount) {
                if (lastFirstVisibleItem < firstVisibleItem) {
                    Toast.makeText(getApplicationContext(),
                            "리스트뷰를 아래로 스크롤 중입니다",
                            Toast.LENGTH_SHORT).show();
                }
                if (lastFirstVisibleItem > firstVisibleItem) {
                    Toast.makeText(getApplicationContext(),
                            "리스트뷰를 위로 스크롤 중입니다",
                            Toast.LENGTH_SHORT).show();
                }
                lastFirstVisibleItem = firstVisibleItem;
            }
        });
    }
}

코드 설명

  • lastFirstVisibleItem 변수: 직전에 화면 상단에 있던 아이템의 인덱스를 저장합니다.
  • onScroll() 콜백: 스크롤이 발생할 때마다 호출되며, 현재 firstVisibleItem과 저장된 값을 비교합니다.
  • 비교 결과: 현재 값이 더 크면 아래로 스크롤, 더 작으면 위로 스크롤한 것으로 판단해 Toast 메시지를 표시합니다.
  • 값 갱신: 비교가 끝나면 lastFirstVisibleItem을 현재 값으로 업데이트해 다음 비교에 대비합니다.

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

앱 실행 및 결과 확인

실제 Android 기기를 컴퓨터에 연결한 상태라고 가정하고 앱을 실행해 보겠습니다. Android Studio에서 프로젝트의 액티비티 파일을 열고 툴바의 Run ▶ 버튼을 클릭하세요. 실행 기기 목록에서 본인의 모바일 기기를 선택하면, 기기 화면에 아래와 같은 기본 화면이 나타납니다.

Android ListView에서 스크롤 방향(위·아래) 감지하는 방법 완벽 가이드

리스트를 손가락으로 밀어 올리면 "리스트뷰를 아래로 스크롤 중입니다", 반대로 내리면 "리스트뷰를 위로 스크롤 중입니다"라는 Toast 메시지가 화면 하단에 표시되는 것을 확인할 수 있습니다.

마무리 및 응용 팁

이처럼 OnScrollListener의 onScroll() 콜백에서 firstVisibleItem 값의 변화만 추적하면 스크롤 방향을 손쉽게 감지할 수 있습니다. 실무에서는 단순히 Toast를 띄우는 것보다 다음과 같이 활용하는 경우가 많습니다.

  • 아래로 스크롤 시 AppBar나 툴바를 자동으로 숨기고, 위로 스크롤 시 다시 표시
  • 플로팅 액션 버튼(FAB)의 표시/숨김 전환
  • 무한 스크롤(페이징) 구현 시 다음 페이지 데이터 미리 불러오기

RecyclerView를 사용하는 프로젝트라면 같은 원리를 OnScrollListener(androidx.recyclerview.widget 패키지)의 onScrolled() 메서드에서 dy 값의 부호로 판별할 수 있으니 참고하세요.