忍者ブログ

2007/03/04開始。ソフトウェア管理やDelerer SEの作者であるおにぎりくんの試行錯誤の記録です。
2024
前の月へ 04 : 12345678910111213141516171819202122232425262728293031 : 06 次の月へ
62  61  60  59  58  57  56  55  54  53  52 
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

前回の記事のものに手を加えました。縦横の画面サイズ切り替えで表示を維持したり、コードからビューを追加してみたりしています。また、名前がアレだったので直しました。


プロジェクト一式ダウンロード

HorizontalPageScrollView.java

package fsoriented.sample.horizontalscroll;

import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.OnGestureListener;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

/**
 * ページごとにスナップする横方向のスクロールビュー。
 *
 * フリック(flingイベント)によって、隣のページにスナップする。
 * ドラッグ(upイベント)によって、一番近いページにスナップする。
 *
 * Gelleryでも似たようなことができそうなんだけど。。。
 *
 * 内部にLinearLayout(horizontal)を持たせることを期待している。
 * LinearLayout直下のビューをページとし、勝手にサイズを変更する。
 *
 * 参考:
 *  - http://www.adamrocker.com/blog/292/we-are-the-speaker-of-google-devfest-2010-japan.html
 *  - http://blog.global-eng.co.jp/android/2011/02/18/horizontalscrollviewにイージングをつける方法/
 *  - http://android.keicode.com/basics/ui-custom-horizontalscrollview.php
 *
 * @author LapisCactus
 *
 */
public class HorizontalPageScrollView extends HorizontalScrollView implements OnGestureListener {

	/** フリックを検出するオブジェクト */
	private GestureDetector detector;
	/** フリックとみなさない速度の閾値 */
	private final float velocityThreshold = 100f;
	/** 現在のページ */
	private int currentPage = 0;
	/** ページ遷移時に呼ばれるリスナー。nullなら呼ばない。 */
	private OnPageChangeListener listener = null;

	/**
	 * コンストラクタ
	 *
	 * @param context
	 */
	public HorizontalPageScrollView(Context context) {
		super(context);
		initialize(context);
	}

	/**
	 * コンストラクタ
	 *
	 * @param context
	 * @param attrs
	 */
	public HorizontalPageScrollView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initialize(context);
	}

	// フリックを検出するオブジェクトを生成する
	private void initialize(Context context) {
		detector = new GestureDetector(context, this);
		this.setFadingEdgeLength(0);
		this.setHorizontalScrollBarEnabled(false);
	}

	/**
	 * 現在表示されているページ番号を返す
	 *
	 * @return ページ番号
	 */
	public int getCurrentPage() {
		return currentPage;
	}

	/**
	 * 指定したページを表示する
	 *
	 * @param page
	 *            ページ番号
	 * @param event
	 *            ページ遷移イベントを発生させるかどうか
	 */
	public void setCurrentPage(int page, boolean event) {
		// サイズが確定していれば、表示位置を移動する
		int viewWidth = this.getWidth();
		if (viewWidth != 0) {
			scrollTo(page * viewWidth, 0);
		}
		// 現在ページを更新する。サイズが未確定なら、確定時イベント(onLayout)で現在ページへ移動する
		currentPage = page;
		// ページ遷移イベントを送信する
		if (event && listener != null) {
			listener.onPageChanged(currentPage, ((LinearLayout) getChildAt(0))
					.getChildAt(currentPage));
		}
	}

	/**
	 * ページ遷移時に呼ばれるリスナーを登録する。
	 *
	 * @param listener
	 */
	public void setOnPageChangedListener(OnPageChangeListener listener) {
		this.listener = listener;
	}

	// ビューのサイズが変更されたときの処理
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		// ページの幅と高さを設定する
		int count = ((LinearLayout) getChildAt(0)).getChildCount();
		for (int i = 0; i < count; i++) {
			((LinearLayout) getChildAt(0)).getChildAt(i).setLayoutParams(
					new android.widget.LinearLayout.LayoutParams(w, h));
		}
	}

	// レイアウトされたときの処理
	// currentPageの適用はここで行う。
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		// カレントページに移動
		int w = r - l;
		smoothScrollTo(currentPage * w, 0);
	}

	// 画面にタッチしたときの処理
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// フリック判定を優先して行う
		if (detector.onTouchEvent(event))
			return true;

		// フリックでなければ、指が離れたイベントを使用する
		switch (event.getAction()) {
		case MotionEvent.ACTION_UP:

			// もっとも近い境界へスナップする
			int currentx = this.getScrollX();
			int viewWidth = this.getWidth();
			int destPage = (currentx + viewWidth / 2) / viewWidth;
			smoothScrollTo(destPage * viewWidth, 0);
			if (currentPage != destPage) {
				currentPage = destPage;
				listener.onPageChanged(currentPage, ((LinearLayout) getChildAt(0))
						.getChildAt(currentPage));
			}
			return true;
		}

		// そのほかはデフォルト
		return super.onTouchEvent(event);
	}

	// フリック時の処理
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
		// フリックなら隣に移動
		int currentx = this.getScrollX();
		int viewWidth = this.getWidth();
		if (velocityX < -velocityThreshold) {
			// 右ページ
			int destPage = currentx / viewWidth + 1;
			smoothScrollTo(destPage * viewWidth, 0);
			if (destPage < ((LinearLayout) getChildAt(0)).getChildCount()) {
				currentPage = destPage;
				listener.onPageChanged(currentPage, ((LinearLayout) getChildAt(0))
						.getChildAt(currentPage));
			}
			return true;
		} else if (velocityX > velocityThreshold) {
			// 左ページ
			int destPage = currentx / viewWidth;
			smoothScrollTo(destPage * viewWidth, 0);
			if (destPage >= 0 && destPage != currentPage) {
				currentPage = destPage;
				listener.onPageChanged(currentPage, ((LinearLayout) getChildAt(0))
						.getChildAt(currentPage));
			}
			return true;
		}
		return false;
	}

	// 不使用
	public boolean onDown(MotionEvent e) {
		return false;
	}

	// 不使用
	public void onLongPress(MotionEvent e) {
	}

	// 不使用
	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
		return false;
	}

	// 不使用
	public void onShowPress(MotionEvent e) {
	}

	// 不使用
	public boolean onSingleTapUp(MotionEvent e) {
		return false;
	}

	/**
	 * フリックやドラッグにより、ページが遷移したときに呼ばれるコールバック定義
	 *
	 * @author LapisCactus
	 *
	 */
	public static interface OnPageChangeListener {
		/**
		 * ページが遷移したときの処理
		 *
		 * @param pageIndex
		 *            ページ番号(0~)
		 * @param showing
		 *            表示中のViewオブジェクト
		 */
		public abstract void onPageChanged(int pageIndex, View showing);
	}
}
PR
スポンサード・リンク
この記事にコメントする
Name
Title
Color
Mail
URL
Comment
Pass Vodafone絵文字 i-mode絵文字 Ezweb絵文字
参考にさせて頂きます
フリックで横スクロールする動作を探していました。ヌルッとした動作感が凄く良いです!参考にさせていただきます!
kusakari 2012/03/03(Sat)11:13:34 EDIT
Re:参考にさせて頂きます
ありがとうございます。ソースコードはどうぞご自由にお使いください。
(2012/03/18 00:55)
フリーソフト指向::開発日記 by LapisCactus
忍者ブログ | [PR]

今日の聖書
自己紹介
名前:
LapisCactus (おにぎりくん)
Twitter:
Mail:
onigirikun_hsp@hotmail.com
自己紹介:
ふつうのしすてむえんじにあ。
趣味で「ソフトウェア管理」や
「TextView」を作っています。
仕事でもコンピュータを触っているのに
趣味も同じという。
健康に悪いですね。
カレンダー
04 2024/05 06
S M T W T F S
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
ブログ内検索
最新CM
[03/03 kusakari]
[11/12 おにぎりくん]
[11/08 れい]
アクセス解析