フリックでページ切り替えをしたい
ということで、GPソフトさんのページを参考にして作成してみました。
LinearLayout ではなく、FrameLayout を継承するように変更しています。横幅を親の View から引き継ぎたかったので、FrameLayout の各層の X 座標をずらして表示するようにしています。
package jp.gr.java_conf.inosix.view; import android.content.Context; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import android.widget.Scroller; public class HorizontalFlingView extends FrameLayout { private static int MIN_FLING_MOVE = 40; private GestureDetector mDetector; private Scroller mScroller; public HorizontalFlingView(Context context, AttributeSet attrs) { super(context, attrs); mDetector = new GestureDetector(getContext(), new GestureListener()); mScroller = new Scroller(getContext(), new DecelerateInterpolator()); } protected void onLayout( boolean changed, int left, int top, int right, int bottom) { for (int i = 0; i < getChildCount(); i++) { getChildAt(i).layout( left + i * getWidth(), top, right + i * getWidth(), bottom); } } @Override public boolean onTouchEvent(MotionEvent event) { boolean wasFlinged = mDetector.onTouchEvent(event); if (wasFlinged || event.getAction() != MotionEvent.ACTION_UP) { return true; } int currentX = getScrollX(); scroll(currentX, getWidth() / 2 < currentX % getWidth()); return true; } private void scroll(int currentX, boolean toGreater) { int targetX = targetPage(currentX, toGreater) * getWidth(); mScroller.startScroll(currentX, 0, targetX - currentX, 0); invalidate(); } private int targetPage(int currentX, boolean toGreater) { if (currentX <= 0) { return 0; } if ((getChildCount() - 1) * getWidth() < currentX) { return getChildCount() - 1; } return (currentX / getWidth()) + (toGreater ? 1 : 0); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), 0); } } private class GestureListener extends SimpleOnGestureListener { @Override public boolean onFling( MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (!shouldReact(e1, e2, velocityX, velocityY)) { super.onFling(e1, e2, velocityX, velocityY); return false; } scroll(getScrollX(), velocityX < 0); return true; } private boolean shouldReact( MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { int dx = (int)(e2.getX() - e1.getX()); return MIN_FLING_MOVE <= Math.abs(dx) && Math.abs(velocityY) < Math.abs(velocityX); } @Override public boolean onScroll( MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { scrollBy((int)distanceX, 0); return true; } } }
レイアウトは次のとおりです。FrameLayout を使ってますが、そこは何でもいいです。
<?xml version="1.0" encoding="utf-8"?> <jp.gr.java_conf.inosix.view.HorizontalFlingView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" > ... </FrameLayout> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" > ... </FrameLayout> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" > ... </FrameLayout> </jp.gr.java_conf.inosix.view.HorizontalFlingView>
レイアウトまわりは詳しくないので、使うときは自己責任で。おかしなことしてたら教えてください。