ListViewでOverScrollする


Android SDK 2.3より、OverScrollが利用できるようになりました。OverScrollとは画面端まで到達した段階から遊びを持ってスクロールできる機能で、UIの向上に寄与します。サンプルプロジェクトを用意しました。GoogleCodeのTechBoosterこちら(SVNが必要です)からどうぞ。

OverScrollを使うのに必要な(代表的な)メソッドは以下の通りです

OverScroll関連メソッド

メソッド 概要
View#overScrollBy オーバースクロールに関する設定
View#onOverScrolled オーバースクロールした結果を受け取る
AbsListView#setOverScrollMode モード設定(ListViewなどで有効)


上記のうち、View#overScrollByやView#onOverScrolledがproctectedなメソッドです。そのためオーバースクロールの実装に当たっては、ListViewなどを継承した独自クラスを作成する必要があります。

本記事は

を参考に作成しました。より詳しい情報を必要な場合は、上記サイトを是非、参考にしてみてください。

サンプルコードは続きから

OverScrollの実装(ListView)

まずは、オーバースクロールを実装するため、独自ListViewを作成します。
OverScrollListView.java

public class OverScrollListView extends ListView {
	private final String TAG ="ListView";

	public OverScrollListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		setOverScrollMode(OVER_SCROLL_ALWAYS);
	}
	・・・省略・・・
}

コンストラクタ内でsetOverScrollModeメソッドを呼び出しています。このメソッドはオーバースクロールのモード設定を行います。
6行目のOVER_SCROLL_ALWAYSは、Listがどのような状態であっても常にオーバースクロールを行います。
setOverScrollModeの引数には

  • OVER_SCROLL_ALWAYS (デフォルト)
  • OVER_SCROLL_IF_CONTENT_SCROLLS(スクロールが必要な時にのみ許可)
  • OVER_SCROLL_NEVER(許可しない)

が指定できます。OVER_SCROLL_IF_CONTENT_SCROLLSでは表示したいコンテンツが画面より大きくて、はみ出ている、またはスクロールバーが表示されているときにのみオーバースクロールを許可します。

以下のようなリストをもったActivityでオーバースクロールを使ってみます。

(Android2.3ではListViewの端に到達した場合、標準ではオレンジ色に発光するエフェクトが追加されました)。

overScrollByをオーバーライドする

	// オーバースクロール
	@Override
	protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,
			int scrollY, int scrollRangeX, int scrollRangeY,
			int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {

		// オーバーライド
		return super.overScrollBy(0, deltaY, 0, scrollY, 0, scrollRangeY, 0,
				200, isTouchEvent);
	}

overScrollByメソッドは9つの引数を持ちます。これはX座標とY座標それぞれに指定が可能なためで、実際の要素として5つに分類できます。
詳しくは以下の表を確認してください。

overScrollByメソッドの引数

引数 概要
int deltaX スクロールした量のX方向の増分
int deltaY スクロールした量のY方向の増分
int scrollX X座標方向の量(今までのスクロールした総和)
int scrollY Y座標方向の量(今までのスクロールした総和)
int scrollRangeX X座標方向のスクロール幅
int scrollRangeY Y座標方向のスクロール幅
int maxOverScrollX X座標方向のスクロール最大量
int maxOverScrollY Y座標方向のスクロール最大量
boolean isTouchEvent TouchEventからの呼び出しフラグ


第9引数のisTouchEventはonTouchEventからの呼び出しを判定するためのフラグです。overScrollByメソッドは実装者が独自に呼び出すことが可能な一方、onTouchEventの処理中にフレームワークによって呼び出されています。このような理由から第9引数は、onTouchEventでの処理と重複しないために必要とされています。

サンプルでは簡略化のためY座標(縦方向)に対する操作のみを行います。サンプルコードではXに関する要素を0として、Yに関する要素を残しています。第8引数のmaxOverScrollYに関しては互換性維持のためフレームワークのデフォルトでは0、つまりオーバースクロールしない設定になっています。ここは適当な値(今回は200)に変更します。

onOverScrolledメソッドの実装

オーバースクロールした結果はonOverScrolledメソッドに通知されます。サンプルではデバッグ表示のためにのみ利用しています。

	//オーバースクロール発生時
	@Override
	protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
			boolean clampedY) {

		Log.v("myListView", "scrollX:" + scrollX + " scrollY:" + scrollY
				+ " clampedX:" + clampedX + " clampedY:" + clampedX);

		super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);

	}

サンプルをAndroid2.3に対応した端末で動かした場合、以下のようなログ出力になります。
LogCat

01-03 17:27:55.799: VERBOSE/ListView(330): scrollX:0 scrollY:0 clampedX:false clampedY:false
01-03 17:27:56.549: VERBOSE/ListView(330): scrollX:0 scrollY:10 clampedX:false clampedY:false
01-03 17:27:56.630: VERBOSE/ListView(330): scrollX:0 scrollY:28 clampedX:false clampedY:false
01-03 17:27:56.699: VERBOSE/ListView(330): scrollX:0 scrollY:52 clampedX:false clampedY:false
01-03 17:27:56.769: VERBOSE/ListView(330): scrollX:0 scrollY:89 clampedX:false clampedY:false
01-03 17:27:56.839: VERBOSE/ListView(330): scrollX:0 scrollY:116 clampedX:false clampedY:false
01-03 17:27:56.909: VERBOSE/ListView(330): scrollX:0 scrollY:138 clampedX:false clampedY:false
01-03 17:27:56.979: VERBOSE/ListView(330): scrollX:0 scrollY:149 clampedX:false clampedY:false
01-03 17:27:57.049: VERBOSE/ListView(330): scrollX:0 scrollY:165 clampedX:false clampedY:false
01-03 17:27:57.119: VERBOSE/ListView(330): scrollX:0 scrollY:192 clampedX:false clampedY:false
01-03 17:27:57.189: VERBOSE/ListView(330): scrollX:0 scrollY:200 clampedX:false clampedY:false

補足

独自Viewを使う際はレイアウトファイルでのListViewの指定が多少変わった書き方にしないと動きません。
layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	<org.jpn.techbooster.sample.overscrolledActivity.OverScrollListView
		android:id="@+id/listView" android:layout_width="fill_parent"
		android:layout_height="wrap_content" />
</LinearLayout>

また、ListViewにつかうArrayに関しては詳細を省略しています。Activity側のコードは以下の通りです。
OverScrolledActivity.java

public class OverScrolledActivity extends Activity {

	private String[] list = {"alpha","bravo","charlie","delta","echo","foxtrot"};

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        //適当なArrayAdapterを作成。今回は簡単なStringで。
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);

        //Listにアダプタを設定
        OverScrollListView listView = (OverScrollListView) findViewById(R.id.listView);
        listView.setAdapter(adapter);

        //Debug用にコールバックを登録
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
            	OverScrollListView listView = (OverScrollListView) parent;

            	//ItemをToastで表示
                String item = (String) listView.getItemAtPosition(position);
                Toast.makeText(OverScrolledActivity.this, item, Toast.LENGTH_LONG).show();
            }
        });
    }
}

Listの要素、ArrayAdapterの接続方法などは通常のListViewとかわりありません。