summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorAdam Powell <adamp@google.com>2014-05-02 10:04:38 -0700
committerAdam Powell <adamp@google.com>2014-05-02 17:16:04 -0700
commit96d62aff0f97557bc15de98e145d9345288d060c (patch)
tree8e985f193fe9ccf476fe55dfed4c820fa5e2eef3 /core
parent5b8b92002230525317d5832a84d7813467594967 (diff)
downloadframeworks_base-96d62aff0f97557bc15de98e145d9345288d060c.zip
frameworks_base-96d62aff0f97557bc15de98e145d9345288d060c.tar.gz
frameworks_base-96d62aff0f97557bc15de98e145d9345288d060c.tar.bz2
ListViews in ScrollViews and ScrollViews in ListViews
Dogs and cats, living together, mass hysteria! Add support for nested scrolling to AbsListView, granting the ability to both ListView and GridView. Change-Id: Iae5c0518c5cebe41927720a7a27916ef13ca706e
Diffstat (limited to 'core')
-rw-r--r--core/java/android/widget/AbsListView.java145
-rw-r--r--core/java/android/widget/ScrollView.java10
2 files changed, 118 insertions, 37 deletions
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 0966be3..3fac883 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -611,6 +611,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final boolean[] mIsScrap = new boolean[1];
+ private final int[] mScrollOffset = new int[2];
+ private final int[] mScrollConsumed = new int[2];
+
// True when the popup should be hidden because of a call to
// dispatchDisplayHint()
private boolean mPopupHidden;
@@ -3264,13 +3267,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
- private boolean startScrollIfNeeded(int y) {
+ private boolean startScrollIfNeeded(int y, MotionEvent vtev) {
// Check if we have moved far enough that it looks more like a
// scroll than a tap
final int deltaY = y - mMotionY;
final int distance = Math.abs(deltaY);
final boolean overscroll = mScrollY != 0;
- if (overscroll || distance > mTouchSlop) {
+ if ((overscroll || distance > mTouchSlop) &&
+ (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {
createScrollingCache();
if (overscroll) {
mTouchMode = TOUCH_MODE_OVERSCROLL;
@@ -3292,17 +3296,28 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
- scrollIfNeeded(y);
+ scrollIfNeeded(y, vtev);
return true;
}
return false;
}
- private void scrollIfNeeded(int y) {
- final int rawDeltaY = y - mMotionY;
+ private void scrollIfNeeded(int y, MotionEvent vtev) {
+ int rawDeltaY = y - mMotionY;
+ if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) {
+ rawDeltaY -= mScrollConsumed[1];
+ mMotionCorrection -= mScrollOffset[1];
+ if (mLastY != Integer.MIN_VALUE) {
+ mLastY -= mScrollOffset[1] + mScrollConsumed[1];
+ }
+ if (vtev != null) {
+ vtev.offsetLocation(0, mScrollOffset[1]);
+ }
+ }
final int deltaY = rawDeltaY - mMotionCorrection;
int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
+ int lastYCorrection = 0;
if (mTouchMode == TOUCH_MODE_SCROLL) {
if (PROFILE_SCROLLING) {
@@ -3361,39 +3376,46 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
int overscroll = -incrementalDeltaY -
(motionViewRealTop - motionViewPrevTop);
- overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
- 0, mOverscrollDistance, true);
- if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
- // Don't allow overfling if we're at the edge.
- if (mVelocityTracker != null) {
- mVelocityTracker.clear();
+ if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll,
+ mScrollOffset)) {
+ mMotionCorrection -= mScrollOffset[1];
+ lastYCorrection -= mScrollOffset[1];
+ vtev.offsetLocation(0, mScrollOffset[1]);
+ } else {
+ overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
+ 0, mOverscrollDistance, true);
+ if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
+ // Don't allow overfling if we're at the edge.
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
}
- }
- final int overscrollMode = getOverScrollMode();
- if (overscrollMode == OVER_SCROLL_ALWAYS ||
- (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
- !contentFits())) {
- mDirection = 0; // Reset when entering overscroll.
- mTouchMode = TOUCH_MODE_OVERSCROLL;
- if (rawDeltaY > 0) {
- mEdgeGlowTop.onPull((float) overscroll / getHeight());
- if (!mEdgeGlowBottom.isFinished()) {
- mEdgeGlowBottom.onRelease();
- }
- invalidate(mEdgeGlowTop.getBounds(false));
- } else if (rawDeltaY < 0) {
- mEdgeGlowBottom.onPull((float) overscroll / getHeight());
- if (!mEdgeGlowTop.isFinished()) {
- mEdgeGlowTop.onRelease();
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
+ !contentFits())) {
+ mDirection = 0; // Reset when entering overscroll.
+ mTouchMode = TOUCH_MODE_OVERSCROLL;
+ if (deltaY > 0) {
+ mEdgeGlowTop.onPull((float) overscroll / getHeight());
+ if (!mEdgeGlowBottom.isFinished()) {
+ mEdgeGlowBottom.onRelease();
+ }
+ invalidate(mEdgeGlowTop.getBounds(false));
+ } else if (deltaY < 0) {
+ mEdgeGlowBottom.onPull((float) overscroll / getHeight());
+ if (!mEdgeGlowTop.isFinished()) {
+ mEdgeGlowTop.onRelease();
+ }
+ invalidate(mEdgeGlowBottom.getBounds(true));
}
- invalidate(mEdgeGlowBottom.getBounds(true));
}
}
}
mMotionY = y;
}
- mLastY = y;
+ mLastY = y + lastYCorrection;
}
} else if (mTouchMode == TOUCH_MODE_OVERSCROLL) {
if (y != mLastY) {
@@ -3517,6 +3539,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return false;
}
+ startNestedScroll(SCROLL_AXIS_VERTICAL);
+
if (mFastScroll != null) {
boolean intercepted = mFastScroll.onTouchEvent(ev);
if (intercepted) {
@@ -3525,7 +3549,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(ev);
+ final MotionEvent vtev = MotionEvent.obtain(ev);
final int actionMasked = ev.getActionMasked();
switch (actionMasked) {
@@ -3535,7 +3559,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
case MotionEvent.ACTION_MOVE: {
- onTouchMove(ev);
+ onTouchMove(ev, vtev);
break;
}
@@ -3586,6 +3610,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
+ if (mVelocityTracker != null) {
+ mVelocityTracker.addMovement(vtev);
+ }
+ vtev.recycle();
return true;
}
@@ -3652,7 +3680,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
- private void onTouchMove(MotionEvent ev) {
+ private void onTouchMove(MotionEvent ev, MotionEvent vtev) {
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
pointerIndex = 0;
@@ -3673,7 +3701,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
case TOUCH_MODE_DONE_WAITING:
// Check if we have moved far enough that it looks more like a
// scroll than a tap. If so, we'll enter scrolling mode.
- if (startScrollIfNeeded(y)) {
+ if (startScrollIfNeeded(y, vtev)) {
break;
}
// Otherwise, check containment within list bounds. If we're
@@ -3693,7 +3721,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
break;
case TOUCH_MODE_SCROLL:
case TOUCH_MODE_OVERSCROLL:
- scrollIfNeeded(y);
+ scrollIfNeeded(y, vtev);
break;
}
}
@@ -3935,6 +3963,49 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
@Override
+ public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+ return ((nestedScrollAxes & SCROLL_AXIS_VERTICAL) != 0);
+ }
+
+ @Override
+ public void onNestedScrollAccepted(View child, View target, int axes) {
+ super.onNestedScrollAccepted(child, target, axes);
+ startNestedScroll(SCROLL_AXIS_VERTICAL);
+ }
+
+ @Override
+ public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
+ int dxUnconsumed, int dyUnconsumed) {
+ final int motionIndex = getChildCount() / 2;
+ final View motionView = getChildAt(motionIndex);
+ final int oldTop = motionView != null ? motionView.getTop() : 0;
+ if (motionView == null || trackMotionScroll(-dyUnconsumed, -dyUnconsumed)) {
+ int myUnconsumed = dyUnconsumed;
+ int myConsumed = 0;
+ if (motionView != null) {
+ myConsumed = motionView.getTop() - oldTop;
+ myUnconsumed -= myConsumed;
+ }
+ dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null);
+ }
+ }
+
+ @Override
+ public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
+ final int childCount = getChildCount();
+ if (!consumed && childCount > 0 && canScrollList((int) velocityY) &&
+ Math.abs(velocityY) > mMinimumVelocity) {
+ reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
+ if (mFlingRunnable == null) {
+ mFlingRunnable = new FlingRunnable();
+ }
+ mFlingRunnable.start((int) velocityY);
+ return true;
+ }
+ return dispatchNestedFling(velocityX, velocityY, consumed);
+ }
+
+ @Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mEdgeGlowTop != null) {
@@ -4070,6 +4141,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mLastY = Integer.MIN_VALUE;
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
+ startNestedScroll(SCROLL_AXIS_VERTICAL);
if (touchMode == TOUCH_MODE_FLING) {
return true;
}
@@ -4087,7 +4159,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final int y = (int) ev.getY(pointerIndex);
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
- if (startScrollIfNeeded(y)) {
+ if (startScrollIfNeeded(y, null)) {
return true;
}
break;
@@ -4101,6 +4173,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mActivePointerId = INVALID_POINTER;
recycleVelocityTracker();
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+ stopNestedScroll();
break;
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 3e46f68..8dfef25a 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -583,7 +583,8 @@ public class ScrollView extends FrameLayout {
@Override
public boolean onTouchEvent(MotionEvent ev) {
initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(ev);
+
+ MotionEvent vtev = MotionEvent.obtain(ev);
final int action = ev.getAction();
@@ -628,6 +629,7 @@ public class ScrollView extends FrameLayout {
int deltaY = mLastMotionY - y;
if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
deltaY -= mScrollConsumed[1] + mScrollOffset[1];
+ vtev.offsetLocation(0, mScrollOffset[1]);
}
if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
final ViewParent parent = getParent();
@@ -663,6 +665,7 @@ public class ScrollView extends FrameLayout {
final int unconsumedY = deltaY - scrolledDeltaY;
if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {
mLastMotionY -= mScrollOffset[1];
+ vtev.offsetLocation(0, mScrollOffset[1]);
} else if (canOverscroll) {
final int pulledToY = oldY + deltaY;
if (pulledToY < 0) {
@@ -720,6 +723,11 @@ public class ScrollView extends FrameLayout {
mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
break;
}
+
+ if (mVelocityTracker != null) {
+ mVelocityTracker.addMovement(vtev);
+ }
+ vtev.recycle();
return true;
}