diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ca061c6..0701157 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -26,5 +26,8 @@
+
\ No newline at end of file
diff --git a/res/layout/activity_list_horizontal_pager_demo.xml b/res/layout/activity_list_horizontal_pager_demo.xml
new file mode 100644
index 0000000..2f92f4f
--- /dev/null
+++ b/res/layout/activity_list_horizontal_pager_demo.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_tabbed_horizontal_pager_demo.xml b/res/layout/activity_tabbed_horizontal_pager_demo.xml
index ce75f95..a0ee63e 100644
--- a/res/layout/activity_tabbed_horizontal_pager_demo.xml
+++ b/res/layout/activity_tabbed_horizontal_pager_demo.xml
@@ -30,7 +30,7 @@
android:text="#3" />
@@ -71,9 +71,20 @@
android:background="#00f" />
-
+
+
+
+
diff --git a/res/values/ids.xml b/res/values/ids.xml
new file mode 100644
index 0000000..636398b
--- /dev/null
+++ b/res/values/ids.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/com/github/ysamlan/horizontalpager/HorizontalPager.java b/src/com/github/ysamlan/horizontalpager/HorizontalPager.java
index 0ccc6cf..5e5ebaa 100644
--- a/src/com/github/ysamlan/horizontalpager/HorizontalPager.java
+++ b/src/com/github/ysamlan/horizontalpager/HorizontalPager.java
@@ -21,6 +21,8 @@
package com.github.ysamlan.horizontalpager;
import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Display;
@@ -30,6 +32,8 @@
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
import android.widget.Scroller;
/**
@@ -70,6 +74,7 @@ public final class HorizontalPager extends ViewGroup {
private static final int SNAP_VELOCITY_DIP_PER_SECOND = 600;
// Argument to getVelocity for units to give pixels per second (1 = pixels per millisecond).
private static final int VELOCITY_UNIT_PIXELS_PER_SECOND = 1000;
+ private static final float INTERPOLATION_FACTOR = 1.75f;
private static final int TOUCH_STATE_REST = 0;
private static final int TOUCH_STATE_HORIZONTAL_SCROLLING = 1;
@@ -124,7 +129,9 @@ public HorizontalPager(final Context context, final AttributeSet attrs) {
* Sets up the scroller and touch/fling sensitivity parameters for the pager.
*/
private void init() {
- mScroller = new Scroller(getContext());
+ // use a DecelerateInterpolator to have more natural movement on long horizontal scrolls
+ Interpolator interpolator = new DecelerateInterpolator(INTERPOLATION_FACTOR);
+ mScroller = new Scroller(getContext(), interpolator);
// Calculate the density-dependent snap velocity in pixels
DisplayMetrics displayMetrics = new DisplayMetrics();
@@ -138,6 +145,22 @@ private void init() {
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
}
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ final SavedState state = new SavedState(super.onSaveInstanceState());
+ state.mCurrentScreen = mCurrentScreen;
+ return state;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState savedState = (SavedState) state;
+ super.onRestoreInstanceState(savedState.getSuperState());
+ if (savedState.mCurrentScreen != -1) {
+ mCurrentScreen = savedState.mCurrentScreen;
+ }
+ }
+
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -162,9 +185,7 @@ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec
if (mFirstLayout) {
scrollTo(mCurrentScreen * width, 0);
mFirstLayout = false;
- }
-
- else if (width != mLastSeenLayoutWidth) { // Width has changed
+ } else if (width != mLastSeenLayoutWidth) { // Width has changed
/*
* Recalculate the width and scroll to the right position to be sure we're in the right
* place in the event that we had a rotation that didn't result in an activity restart
@@ -203,77 +224,39 @@ protected void onLayout(final boolean changed, final int l, final int t, final i
@Override
public boolean onInterceptTouchEvent(final MotionEvent ev) {
- /*
- * By Yoni Samlan: Modified onInterceptTouchEvent based on standard ScrollView's
- * onIntercept. The logic is designed to support a nested vertically scrolling view inside
- * this one; once a scroll registers for X-wise scrolling, handle it in this view and don't
- * let the children, but once a scroll registers for y-wise scrolling, let the children
- * handle it exclusively.
- */
final int action = ev.getAction();
boolean intercept = false;
switch (action) {
case MotionEvent.ACTION_MOVE:
- /*
- * If we're in a horizontal scroll event, take it (intercept further events). But if
- * we're mid-vertical-scroll, don't even try; let the children deal with it. If we
- * haven't found a scroll event yet, check for one.
- */
- if (mTouchState == TOUCH_STATE_HORIZONTAL_SCROLLING) {
- /*
- * We've already started a horizontal scroll; set intercept to true so we can
- * take the remainder of all touch events in onTouchEvent.
- */
- intercept = true;
- } else if (mTouchState == TOUCH_STATE_VERTICAL_SCROLLING) {
- // Let children handle the events for the duration of the scroll event.
- intercept = false;
- } else { // We haven't picked up a scroll event yet; check for one.
-
- /*
- * If we detected a horizontal scroll event, start stealing touch events (mark
- * as scrolling). Otherwise, see if we had a vertical scroll event -- if so, let
- * the children handle it and don't look to intercept again until the motion is
- * done.
- */
-
- final float x = ev.getX();
- final int xDiff = (int) Math.abs(x - mLastMotionX);
- boolean xMoved = xDiff > mTouchSlop;
-
- if (xMoved) {
- // Scroll if the user moved far enough along the X axis
- mTouchState = TOUCH_STATE_HORIZONTAL_SCROLLING;
- mLastMotionX = x;
- }
-
- final float y = ev.getY();
- final int yDiff = (int) Math.abs(y - mLastMotionY);
- boolean yMoved = yDiff > mTouchSlop;
+ final int xDiff = (int) Math.abs(ev.getX() - mLastMotionX);
+ if (xDiff > mTouchSlop) {
+ mTouchState = TOUCH_STATE_HORIZONTAL_SCROLLING;
+ mLastMotionX = ev.getX();
+ }
- if (yMoved) {
- mTouchState = TOUCH_STATE_VERTICAL_SCROLLING;
- }
+ final int yDiff = (int) Math.abs(ev.getY() - mLastMotionY);
+ if (yDiff > mTouchSlop) {
+ mTouchState = TOUCH_STATE_VERTICAL_SCROLLING;
}
+ if ((Math.abs(xDiff * 2) > Math.abs(yDiff)) && (xDiff > mTouchSlop)) {
+ intercept = true;
+ }
break;
- case MotionEvent.ACTION_CANCEL:
+
case MotionEvent.ACTION_UP:
- // Release the drag.
mTouchState = TOUCH_STATE_REST;
break;
+
case MotionEvent.ACTION_DOWN:
- /*
- * No motion yet, but register the coordinates so we can check for intercept at the
- * next MOVE event.
- */
mLastMotionY = ev.getY();
mLastMotionX = ev.getX();
break;
+
default:
break;
- }
+ }
return intercept;
}
@@ -501,4 +484,38 @@ public static interface OnScreenSwitchListener {
*/
void onScreenSwitched(int screen);
}
+
+ /**
+ * A parcelable so we can save our current screen and return to it after an activity destroy.
+ */
+ public static class SavedState extends BaseSavedState {
+
+ private int mCurrentScreen = -1;
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ public SavedState(Parcel in) {
+ super(in);
+ mCurrentScreen = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(mCurrentScreen);
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
}
diff --git a/src/com/github/ysamlan/horizontalpager/HorizontalPagerDemo.java b/src/com/github/ysamlan/horizontalpager/HorizontalPagerDemo.java
index 82d2406..36e3162 100644
--- a/src/com/github/ysamlan/horizontalpager/HorizontalPagerDemo.java
+++ b/src/com/github/ysamlan/horizontalpager/HorizontalPagerDemo.java
@@ -20,6 +20,7 @@ public void onCreate(final Bundle savedInstanceState) {
// Create the view switcher
HorizontalPager realViewSwitcher = new HorizontalPager(getApplicationContext());
+ realViewSwitcher.setId(R.id.horizontal_pager);
// Add some views to it
final int[] backgroundColors =
diff --git a/src/com/github/ysamlan/horizontalpager/ListHorizontalPagerDemo.java b/src/com/github/ysamlan/horizontalpager/ListHorizontalPagerDemo.java
new file mode 100644
index 0000000..8c2a94c
--- /dev/null
+++ b/src/com/github/ysamlan/horizontalpager/ListHorizontalPagerDemo.java
@@ -0,0 +1,65 @@
+package com.github.ysamlan.horizontalpager;
+
+import java.util.Random;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+/**
+ * An example of a HorizontalPager used to display a few ListViews side by side.
+ *
+ * @author Gil Shapira
+ */
+public class ListHorizontalPagerDemo extends Activity {
+
+ private static final int LIST_SIZE = 50;
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_list_horizontal_pager_demo);
+
+ // set an adapter in each of the ListViews defined in the XML
+ ListView list1 = (ListView) findViewById(R.id.list1);
+ ListView list2 = (ListView) findViewById(R.id.list2);
+ ListView list3 = (ListView) findViewById(R.id.list3);
+ list1.setAdapter(generateAdapter());
+ list2.setAdapter(generateAdapter());
+ list3.setAdapter(generateAdapter());
+ }
+
+ /**
+ * @return an adapter that serves random strings in TextViews.
+ */
+ private ListAdapter generateAdapter() {
+ String[] strings = generateStrings();
+ ListAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, strings);
+ return adapter;
+ }
+
+ /**
+ * @return an array full of random lorem ipsum words.
+ */
+ private String[] generateStrings() {
+ // make a collection of words from the lorem ipsum text
+ if (sWords == null) {
+ sWords = getString(R.string.lipsum).split(" ");
+ }
+
+ Random rand = new Random();
+ String[] strings = new String[LIST_SIZE];
+ for (int i = 0; i < LIST_SIZE; i++) {
+ // pick a random word from the array
+ int w = rand.nextInt(sWords.length);
+ strings[i] = sWords[w];
+ }
+
+ return strings;
+ }
+
+ private static String[] sWords = null;
+
+}
diff --git a/src/com/github/ysamlan/horizontalpager/TabbedHorizontalPagerDemo.java b/src/com/github/ysamlan/horizontalpager/TabbedHorizontalPagerDemo.java
index 1b205e4..721e7b0 100644
--- a/src/com/github/ysamlan/horizontalpager/TabbedHorizontalPagerDemo.java
+++ b/src/com/github/ysamlan/horizontalpager/TabbedHorizontalPagerDemo.java
@@ -19,9 +19,7 @@ public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tabbed_horizontal_pager_demo);
mPager = (HorizontalPager) findViewById(R.id.horizontal_pager);
- mPager.setOnScreenSwitchListener(onScreenSwitchListener);
mRadioGroup = (RadioGroup) findViewById(R.id.tabs);
- mRadioGroup.setOnCheckedChangeListener(onCheckedChangedListener);
// Go to the simple demo if the user clicks on the simple demo button
findViewById(R.id.simple_demo_btn).setOnClickListener(new View.OnClickListener() {
@@ -31,6 +29,22 @@ public void onClick(final View v) {
HorizontalPagerDemo.class));
}
});
+
+ // Go to the list demo if the user clicks on the list demo button
+ findViewById(R.id.list_demo_btn).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ startActivity(new Intent(TabbedHorizontalPagerDemo.this,
+ ListHorizontalPagerDemo.class));
+ }
+ });
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mPager.setOnScreenSwitchListener(onScreenSwitchListener);
+ mRadioGroup.setOnCheckedChangeListener(onCheckedChangedListener);
}
private final HorizontalPager.OnScreenSwitchListener onScreenSwitchListener =