【Android開発】ScrollViewのスクロール終了(最下部・最上部への到達)を検出する方法
この記事では、Androidアプリ開発においてScrollViewのスクロール終了、つまりユーザーがコンテンツの最下部までスクロールしたことを検出する方法を、実際のコード例とともに分かりやすく解説します。
「記事を最後まで読んだらポイントを付与したい」「無限スクロールで次のページを読み込みたい」など、スクロール位置の検出はさまざまな場面で活用できるテクニックです。今回は OnScrollChangedListener を使って、最下部だけでなく最上部への到達も同時に検出する実装方法を紹介します。
全体の流れ
今回作成するアプリは、ScrollView内にWebViewを配置し、長いテキストデータを表示します。スクロール位置が変化するたびにリスナーが呼ばれ、最下部または最上部に到達したタイミングでToastメッセージを表示する仕組みです。
ステップ1:新規プロジェクトの作成
まず、Android Studioで新しいプロジェクトを作成します。メニューから File → New Project を選択し、必要な項目を入力してプロジェクトを作成してください。テンプレートは「Empty Activity」で問題ありません。
ステップ2:レイアウトファイル(activity_main.xml)の編集
res/layout/activity_main.xml に以下のコードを記述します。RelativeLayoutの中にScrollViewを配置し、その中にWebViewを入れます。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context=".MainActivity">
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</WebView>
</ScrollView>
</RelativeLayout>ここではScrollViewの子要素としてWebViewを使用していますが、TextViewやLinearLayoutなど他のViewでも同じ考え方で実装できます。
ステップ3:MainActivity.javaの実装
次に、src/MainActivity.java に以下のコードを追加します。
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.webkit.WebView;
import android.widget.ScrollView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnTouchListener,
ViewTreeObserver.OnScrollChangedListener {
ScrollView scrollView;
WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
scrollView = findViewById(R.id.scrollView);
webView = findViewById(R.id.webView);
scrollView.setOnTouchListener(this);
scrollView.getViewTreeObserver().addOnScrollChangedListener(this);
webView.loadData(getResources().getString(R.string.sample_data), "text/html", null);
}
public void onScrollChanged() {
View view = scrollView.getChildAt(scrollView.getChildCount() - 1);
int topDetector = scrollView.getScrollY();
int bottomDetector = view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY());
if (bottomDetector == 0) {
Toast.makeText(getBaseContext(), "Scroll View bottom reached", Toast.LENGTH_SHORT).show();
}
if (topDetector <= 0) {
Toast.makeText(getBaseContext(), "Scroll View top reached", Toast.LENGTH_SHORT).show();
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
}コードのポイント解説
このコードの核心は onScrollChanged() メソッドです。仕組みを詳しく見てみましょう。
- bottomDetector の計算: 子Viewの底辺の座標(
view.getBottom())から「画面の高さ+現在のスクロール量」を引くことで、あとどれだけ下にコンテンツが残っているかを算出しています。この値が0になれば、最下部までスクロールしたことになります。 - topDetector の計算: 現在のスクロール位置(
getScrollY())が0以下であれば、最上部にいることを意味します。
なお、上記コードは旧サポートライブラリ(android.support.v7.app.AppCompatActivity)を使用しています。現在のAndroid Studioで新規プロジェクトを作成した場合は、AndroidXの androidx.appcompat.app.AppCompatActivity に置き換えてください。動作内容は同じです。
ステップ4:表示用テキスト(strings.xml)の追加
res/values/strings.xml を開き、WebViewに読み込ませる長めのHTMLテキストを追加します。スクロール検出をテストするために、ある程度長い文章を用意しておきます。
<resources>
<string name="app_name">Sample</string>
<string name="sample_data">
<![CDATA[<html>]]>
<![CDATA[<style>]]>
p.small {
line-height: 1.5;
font-size: 12px;
}
<![CDATA[</style>]]>
<![CDATA[<body><p align="justify" class="small">]]>
(ここに複数段落のダミーテキストを挿入します)
<![CDATA[</p></body></html>]]>
</string>
</resources>実際のプロジェクトでは、上記の「ダミーテキスト」部分に任意の長文を入れてください。CDATAセクションで囲むことで、HTMLタグをそのまま文字列として扱えます。
ステップ5:AndroidManifest.xmlの確認
最後に、androidManifest.xml に以下のようにMainActivityがランチャーアクティビティとして登録されていることを確認します。
<?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)アイコンをクリックしてください。デバイス選択ダイアログが表示されたら、接続したモバイル端末を選択します。
アプリが起動したら、画面を上下にスクロールしてみてください。コンテンツの最下部に到達すると「Scroll View bottom reached」というToastが表示され、最上部に戻ると「Scroll View top reached」が表示されます。

まとめ
本記事では、ViewTreeObserver.OnScrollChangedListener を活用してScrollViewのスクロール位置を監視し、最上部・最下部への到達を検出する方法を解説しました。
この手法は、RecyclerViewなどを使った無限スクロールの実装や、「続きを読む」ボタンの表示制御、チュートリアルの読了判定など、幅広い用途に応用できます。ぜひご自身のプロジェクトでも試してみてください。
-
AndroidでプログラムからScrollViewを無効化する方法
このチュートリアルでは、AndroidアプリでScrollViewをプログラム的に無効化する方法を解説します。setOnTouchListener()を使ってタッチイベントを横取りすることで、ユーザーによるスクロール操作を防ぐことができます。 ステップ1:新規プロジェクトの作成 まず、Android Studioで新しいプロジェクトを作成します。メニューから「File」→「New Project」を選択し、必要な項目をすべて入力してプロジェクトを作成してください。 ステップ2:レイアウトファイルの編集(res/layout/activity_main.xml) 以下のコードを res/lay
-
【Android】5秒間のユーザー非アクティブ(無操作)を検出する方法を解説
はじめに 本記事では、Androidアプリでユーザーが5秒間何も操作しなかったこと(非アクティブ状態)を検出する方法を、サンプルコードとともに解説します。 鍵となるのは、Activityクラスに標準で用意されているonUserInteraction()コールバックです。このメソッドはタップやスワイプなど、ユーザーが画面を操作するたびに呼び出されるため、Handlerと組み合わせることで「最後の操作からの経過時間」を簡単に計測できます。 手順1:新規プロジェクトを作成する Android Studioを起動し、メニューから「File」→「New Project」を選択します。必要事項を入力して、