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:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user