diff options
author | Adam Powell <adamp@google.com> | 2010-02-25 11:21:14 -0800 |
---|---|---|
committer | Adam Powell <adamp@google.com> | 2010-02-25 16:05:26 -0800 |
commit | 4cd47702f00a2fd6d6bc16c5398edb884d98bdca (patch) | |
tree | 5b715cb3aca9e6f40fb657d4c35766a9cd3152f3 /core/java | |
parent | 5be65ab5b9c57028f9af70b283b4e43515f5bd04 (diff) | |
download | frameworks_base-4cd47702f00a2fd6d6bc16c5398edb884d98bdca.zip frameworks_base-4cd47702f00a2fd6d6bc16c5398edb884d98bdca.tar.gz frameworks_base-4cd47702f00a2fd6d6bc16c5398edb884d98bdca.tar.bz2 |
HorizontalScrollView multitouch scroll and only grip on content
AbsListView multitouch scroll and only grip on content
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/widget/AbsListView.java | 117 | ||||
-rw-r--r-- | core/java/android/widget/GridView.java | 2 | ||||
-rw-r--r-- | core/java/android/widget/HorizontalScrollView.java | 137 | ||||
-rw-r--r-- | core/java/android/widget/ListView.java | 1 | ||||
-rw-r--r-- | core/java/android/widget/ScrollView.java | 4 |
5 files changed, 208 insertions, 53 deletions
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 9ddfeff..a9b746a 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -467,6 +467,18 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // True when the popup should be hidden because of a call to // dispatchDisplayHint() private boolean mPopupHidden; + + /** + * ID of the active pointer. This is used to retain consistency during + * drags/flings if multiple pointers are used. + */ + private int mActivePointerId = INVALID_POINTER; + + /** + * Sentinel value for no current active pointer. + * Used by {@link #mActivePointerId}. + */ + private static final int INVALID_POINTER = -1; /** * Interface definition for a callback to be invoked when the list or grid @@ -1995,8 +2007,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } final int action = ev.getAction(); - final int x = (int) ev.getX(); - final int y = (int) ev.getY(); View v; int deltaY; @@ -2006,18 +2016,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } mVelocityTracker.addMovement(ev); - switch (action) { + switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { switch (mTouchMode) { case TOUCH_MODE_OVERFLING: { mFlingRunnable.endFling(); mTouchMode = TOUCH_MODE_OVERSCROLL; - mLastY = y; + mLastY = (int) ev.getY(); mMotionCorrection = 0; + mActivePointerId = ev.getPointerId(0); break; } default: { + mActivePointerId = ev.getPointerId(0); + final int x = (int) ev.getX(); + final int y = (int) ev.getY(); int motionPosition = pointToPosition(x, y); if (!mDataChanged) { if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0) @@ -2037,12 +2051,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // code in ViewRoot to try to find a nearby view to select return false; } - // User clicked on whitespace, or stopped a fling. It is a scroll. - createScrollingCache(); - mTouchMode = TOUCH_MODE_SCROLL; - mMotionCorrection = 0; - motionPosition = findMotionRow(y); - reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + + if (mTouchMode == TOUCH_MODE_FLING) { + // Stopped a fling. It is a scroll. + createScrollingCache(); + mTouchMode = TOUCH_MODE_SCROLL; + mMotionCorrection = 0; + motionPosition = findMotionRow(y); + reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + } } } @@ -2062,6 +2079,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } case MotionEvent.ACTION_MOVE: { + final int pointerIndex = ev.findPointerIndex(mActivePointerId); + final int y = (int) ev.getY(pointerIndex); deltaY = y - mMotionY; switch (mTouchMode) { case TOUCH_MODE_DOWN: @@ -2142,7 +2161,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // We did not scroll the full amount. Treat this essentially like the // start of a new touch scroll - final int motionPosition = findMotionRow(y); + final int motionPosition = findClosestMotionRow(y); mMotionCorrection = 0; motionView = getChildAt(motionPosition - mFirstPosition); @@ -2238,7 +2257,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } else { final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - final int initialVelocity = (int) velocityTracker.getYVelocity(); + final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId); if (Math.abs(initialVelocity) > mMinimumVelocity) { if (mFlingRunnable == null) { @@ -2264,7 +2283,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - final int initialVelocity = (int) velocityTracker.getYVelocity(); + final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId); reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); if (Math.abs(initialVelocity) > mMinimumVelocity) { @@ -2290,6 +2309,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mVelocityTracker.recycle(); mVelocityTracker = null; } + + mActivePointerId = INVALID_POINTER; if (PROFILE_SCROLLING) { if (mScrollProfilingStarted) { @@ -2332,6 +2353,24 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mVelocityTracker = null; } } + + mActivePointerId = INVALID_POINTER; + break; + } + + case MotionEvent.ACTION_POINTER_UP: { + onSecondaryPointerUp(ev); + final int x = mMotionX; + final int y = mMotionY; + final int motionPosition = pointToPosition(x, y); + if (motionPosition >= 0) { + // Remember where the motion event started + v = getChildAt(motionPosition - mFirstPosition); + mMotionViewOriginalTop = v.getTop(); + mMotionPosition = motionPosition; + } + mLastY = y; + break; } } @@ -2380,8 +2419,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); - int x = (int) ev.getX(); - int y = (int) ev.getY(); View v; if (mFastScroller != null) { @@ -2391,13 +2428,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } - switch (action) { + switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { int touchMode = mTouchMode; if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) { return true; } + final int x = (int) ev.getX(); + final int y = (int) ev.getY(); + mActivePointerId = ev.getPointerId(0); + int motionPosition = findMotionRow(y); if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) { // User clicked on an actual view (and was not stopping a fling). @@ -2420,6 +2461,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te case MotionEvent.ACTION_MOVE: { switch (mTouchMode) { case TOUCH_MODE_DOWN: + final int pointerIndex = ev.findPointerIndex(mActivePointerId); + final int y = (int) ev.getY(pointerIndex); if (startScrollIfNeeded(y - mMotionY)) { return true; } @@ -2430,13 +2473,37 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te case MotionEvent.ACTION_UP: { mTouchMode = TOUCH_MODE_REST; + mActivePointerId = INVALID_POINTER; reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); break; } + + case MotionEvent.ACTION_POINTER_UP: { + onSecondaryPointerUp(ev); + break; + } } return false; } + + private void onSecondaryPointerUp(MotionEvent ev) { + final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> + MotionEvent.ACTION_POINTER_INDEX_SHIFT; + final int pointerId = ev.getPointerId(pointerIndex); + if (pointerId == mActivePointerId) { + // This was our active pointer going up. Choose a new + // active pointer and adjust accordingly. + // TODO: Make this decision more intelligent. + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + mMotionX = (int) ev.getX(newPointerIndex); + mMotionY = (int) ev.getY(newPointerIndex); + mActivePointerId = ev.getPointerId(newPointerIndex); + if (mVelocityTracker != null) { + mVelocityTracker.clear(); + } + } + } /** * {@inheritDoc} @@ -3150,9 +3217,25 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * Find the row closest to y. This row will be used as the motion row when scrolling * * @param y Where the user touched - * @return The position of the first (or only) item in the row closest to y + * @return The position of the first (or only) item in the row containing y */ abstract int findMotionRow(int y); + + /** + * Find the row closest to y. This row will be used as the motion row when scrolling. + * + * @param y Where the user touched + * @return The position of the first (or only) item in the row closest to y + */ + int findClosestMotionRow(int y) { + final int childCount = getChildCount(); + if (childCount == 0) { + return INVALID_POSITION; + } + + final int motionRow = findMotionRow(y); + return motionRow != INVALID_POSITION ? motionRow : mFirstPosition + childCount - 1; + } /** * Causes all the views to be rebuilt and redrawn. diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index b9acf5e..9e930a5 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -431,8 +431,6 @@ public class GridView extends AbsListView { } } } - - return mFirstPosition + childCount - 1; } return INVALID_POSITION; } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index a7b819a..acb7e02 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -116,7 +116,19 @@ public class HorizontalScrollView extends FrameLayout { private int mTouchSlop; private int mMinimumVelocity; private int mMaximumVelocity; - + + /** + * ID of the active pointer. This is used to retain consistency during + * drags/flings if multiple pointers are used. + */ + private int mActivePointerId = INVALID_POINTER; + + /** + * Sentinel value for no current active pointer. + * Used by {@link #mActivePointerId}. + */ + private static final int INVALID_POINTER = -1; + public HorizontalScrollView(Context context) { this(context, null); } @@ -362,6 +374,17 @@ public class HorizontalScrollView extends FrameLayout { return handled; } + private boolean inChild(int x, int y) { + if (getChildCount() > 0) { + final View child = getChildAt(0); + return !(y < child.getTop() + || y >= child.getBottom() + || x < child.getLeft() + || x >= child.getRight()); + } + return false; + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { /* @@ -380,10 +403,8 @@ public class HorizontalScrollView extends FrameLayout { return true; } - final float x = ev.getX(); - - switch (action) { - case MotionEvent.ACTION_MOVE: + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_MOVE: { /* * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check * whether the user has moved far enough from his original down touch. @@ -393,16 +414,30 @@ public class HorizontalScrollView extends FrameLayout { * Locally do absolute value. mLastMotionX is set to the x value * of the down event. */ + final int pointerIndex = ev.findPointerIndex(mActivePointerId); + final float x = ev.getX(pointerIndex); final int xDiff = (int) Math.abs(x - mLastMotionX); if (xDiff > mTouchSlop) { mIsBeingDragged = true; + mLastMotionX = x; if (mParent != null) mParent.requestDisallowInterceptTouchEvent(true); } break; + } - case MotionEvent.ACTION_DOWN: - /* Remember location of down touch */ + case MotionEvent.ACTION_DOWN: { + final float x = ev.getX(); + if (!inChild((int) x, (int) ev.getY())) { + mIsBeingDragged = false; + break; + } + + /* + * Remember location of down touch. + * ACTION_DOWN always refers to pointer index 0. + */ mLastMotionX = x; + mActivePointerId = ev.getPointerId(0); /* * If being flinged and user touches the screen, initiate drag; @@ -411,11 +446,16 @@ public class HorizontalScrollView extends FrameLayout { */ mIsBeingDragged = !mScroller.isFinished(); break; + } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: /* Release the drag */ mIsBeingDragged = false; + mActivePointerId = INVALID_POINTER; + break; + case MotionEvent.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); break; } @@ -441,10 +481,9 @@ public class HorizontalScrollView extends FrameLayout { mVelocityTracker.addMovement(ev); final int action = ev.getAction(); - final float x = ev.getX(); - switch (action) { - case MotionEvent.ACTION_DOWN: + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: { /* * If being flinged and user touches, stop the fling. isFinished * will be false if being flinged. @@ -452,42 +491,78 @@ public class HorizontalScrollView extends FrameLayout { if (!mScroller.isFinished()) { mScroller.abortAnimation(); } + + final float x = ev.getX(); + if (!(mIsBeingDragged = inChild((int) x, (int) ev.getY()))) { + return false; + } // Remember where the motion event started mLastMotionX = x; break; + } case MotionEvent.ACTION_MOVE: - // Scroll to follow the motion event - final int deltaX = (int) (mLastMotionX - x); - mLastMotionX = x; - - overscrollBy(deltaX, 0, mScrollX, 0, getScrollRange(), 0, - getOverscrollMax(), 0); + if (mIsBeingDragged) { + // Scroll to follow the motion event + final int activePointerIndex = ev.findPointerIndex(mActivePointerId); + final float x = ev.getX(activePointerIndex); + final int deltaX = (int) (mLastMotionX - x); + mLastMotionX = x; + + overscrollBy(deltaX, 0, mScrollX, 0, getScrollRange(), 0, + getOverscrollMax(), 0); + } break; case MotionEvent.ACTION_UP: - final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - int initialVelocity = (int) velocityTracker.getXVelocity(); - - if (getChildCount() > 0) { - if ((Math.abs(initialVelocity) > mMinimumVelocity)) { - fling(-initialVelocity); - } else { - final int right = getScrollRange(); - if (mScroller.springback(mScrollX, mScrollY, 0, 0, right, 0)) { - invalidate(); + if (mIsBeingDragged) { + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId); + + if (getChildCount() > 0) { + if ((Math.abs(initialVelocity) > mMinimumVelocity)) { + fling(-initialVelocity); + } else { + final int right = getScrollRange(); + if (mScroller.springback(mScrollX, mScrollY, 0, 0, right, 0)) { + invalidate(); + } } } - } + + mActivePointerId = INVALID_POINTER; + mIsBeingDragged = false; - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } } + break; + case MotionEvent.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); + break; } return true; } + private void onSecondaryPointerUp(MotionEvent ev) { + final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> + MotionEvent.ACTION_POINTER_INDEX_SHIFT; + final int pointerId = ev.getPointerId(pointerIndex); + if (pointerId == mActivePointerId) { + // This was our active pointer going up. Choose a new + // active pointer and adjust accordingly. + // TODO: Make this decision more intelligent. + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + mLastMotionX = ev.getX(newPointerIndex); + mActivePointerId = ev.getPointerId(newPointerIndex); + if (mVelocityTracker != null) { + mVelocityTracker.clear(); + } + } + } + @Override protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 8d688a5..51a1ef2 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1192,7 +1192,6 @@ public class ListView extends AbsListView { return mFirstPosition + i; } } - return mFirstPosition + childCount - 1; } return INVALID_POSITION; } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index fd24058..489c44d 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -427,7 +427,7 @@ public class ScrollView extends FrameLayout { case MotionEvent.ACTION_DOWN: { final float y = ev.getY(); - if (!inChild((int)ev.getX(), (int)y)) { + if (!inChild((int) ev.getX(), (int) y)) { mIsBeingDragged = false; break; } @@ -493,7 +493,7 @@ public class ScrollView extends FrameLayout { } final float y = ev.getY(); - if (!(mIsBeingDragged = inChild((int)ev.getX(), (int)y))) { + if (!(mIsBeingDragged = inChild((int) ev.getX(), (int) y))) { return false; } |