ScrollView multiple pointer support. Fixed a bug where ScrollView

wouldn't scroll if the initially tapped child ignores the down event.
This commit is contained in:
Adam Powell
2010-02-23 14:49:01 -08:00
parent 618c8f1ede
commit bc4e7533b9

View File

@@ -115,6 +115,18 @@ public class ScrollView 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 ScrollView(Context context) {
this(context, null);
@@ -392,10 +404,8 @@ public class ScrollView extends FrameLayout {
return true;
}
final float y = ev.getY();
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.
@@ -405,21 +415,29 @@ public class ScrollView extends FrameLayout {
* Locally do absolute value. mLastMotionY is set to the y value
* of the down event.
*/
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float y = ev.getY(pointerIndex);
final int yDiff = (int) Math.abs(y - mLastMotionY);
if (yDiff > mTouchSlop) {
mIsBeingDragged = true;
mLastMotionY = y;
}
break;
}
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_DOWN: {
final float y = ev.getY();
if (!inChild((int)ev.getX(), (int)y)) {
mIsBeingDragged = false;
break;
}
/* Remember location of down touch */
/*
* Remember location of down touch.
* ACTION_DOWN always refers to pointer index 0.
*/
mLastMotionY = y;
mActivePointerId = ev.getPointerId(0);
/*
* If being flinged and user touches the screen, initiate drag;
@@ -428,11 +446,16 @@ public class ScrollView 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;
}
@@ -458,10 +481,9 @@ public class ScrollView extends FrameLayout {
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
final float y = ev.getY();
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.
@@ -470,29 +492,33 @@ public class ScrollView extends FrameLayout {
mScroller.abortAnimation();
}
if (!inChild((int)ev.getX(), (int)y)) {
mIsBeingDragged = false;
final float y = ev.getY();
if (!(mIsBeingDragged = inChild((int)ev.getX(), (int)y))) {
return false;
}
// Remember where the motion event started
mLastMotionY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE:
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
final float y = ev.getY(activePointerIndex);
final int deltaY = (int) (mLastMotionY - y);
mLastMotionY = y;
overscrollBy(0, deltaY, 0, mScrollY, 0, getScrollRange(),
0, getOverscrollMax());
break;
}
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity();
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
if (getChildCount() > 0) {
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
@@ -505,15 +531,39 @@ public class ScrollView extends FrameLayout {
}
}
mActivePointerId = INVALID_POINTER;
mIsBeingDragged = false;
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;
mLastMotionY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
if (mVelocityTracker != null) {
mVelocityTracker.clear();
}
}
}
@Override
protected void onOverscrolled(int scrollX, int scrollY,
boolean clampedX, boolean clampedY) {