HorizontalScrollView multitouch scroll and only grip on content
AbsListView multitouch scroll and only grip on content
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -431,8 +431,6 @@ public class GridView extends AbsListView {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mFirstPosition + childCount - 1;
|
||||
}
|
||||
return INVALID_POSITION;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
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);
|
||||
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 (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();
|
||||
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) {
|
||||
|
||||
@@ -1192,7 +1192,6 @@ public class ListView extends AbsListView {
|
||||
return mFirstPosition + i;
|
||||
}
|
||||
}
|
||||
return mFirstPosition + childCount - 1;
|
||||
}
|
||||
return INVALID_POSITION;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user