Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,8 @@
<activity
android:name=".HorizontalPagerDemo"
android:label="@string/app_name" />
<activity
android:name=".ListHorizontalPagerDemo"
android:label="@string/app_name" />
</application>
</manifest>
29 changes: 29 additions & 0 deletions res/layout/activity_list_horizontal_pager_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<com.github.ysamlan.horizontalpager.HorizontalPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/horizontal_pager"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<ListView
android:id="@+id/list1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#ff440000"
android:background="#ff440000" />

<ListView
android:id="@+id/list2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#ff004400"
android:background="#ff004400" />

<ListView
android:id="@+id/list3"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#ff000044"
android:background="#ff000044" />

</com.github.ysamlan.horizontalpager.HorizontalPager>
23 changes: 17 additions & 6 deletions res/layout/activity_tabbed_horizontal_pager_demo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
android:text="#3" />
</RadioGroup>
<com.github.ysamlan.horizontalpager.HorizontalPager
android:id="@+id/horizontal_pager"
android:id="@id/horizontal_pager"
android:layout_width="fill_parent"
android:layout_height="0px"
android:layout_weight="1">
Expand Down Expand Up @@ -71,9 +71,20 @@
android:background="#00f" />
</ScrollView>
</com.github.ysamlan.horizontalpager.HorizontalPager>
<Button
android:id="@+id/simple_demo_btn"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:text="Simple Demo" />
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent">
<Button
android:id="@+id/simple_demo_btn"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:text="Simple Demo" />
<Button
android:id="@+id/list_demo_btn"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:text="List Demo" />
</LinearLayout>
</LinearLayout>
4 changes: 4 additions & 0 deletions res/values/ids.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="horizontal_pager" />
</resources>
131 changes: 74 additions & 57 deletions src/com/github/ysamlan/horizontalpager/HorizontalPager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

/**
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}

public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String>(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;

}
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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 =
Expand Down