Merge "Support fling back in scrolling containers"
This commit is contained in:
committed by
Android (Google) Code Review
commit
828bff7d95
@@ -241,6 +241,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
*/
|
||||
public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
|
||||
|
||||
/**
|
||||
* When flinging the stretch towards scrolling content, it should destretch quicker than the
|
||||
* fling would normally do. The visual effect of flinging the stretch looks strange as little
|
||||
* appears to happen at first and then when the stretch disappears, the content starts
|
||||
* scrolling quickly.
|
||||
*/
|
||||
private static final float FLING_DESTRETCH_FACTOR = 4f;
|
||||
|
||||
/**
|
||||
* The thread that created this view.
|
||||
*/
|
||||
@@ -4216,9 +4224,23 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
// fling further.
|
||||
boolean flingVelocity = Math.abs(initialVelocity) > mMinimumVelocity;
|
||||
if (flingVelocity && !mEdgeGlowTop.isFinished()) {
|
||||
mEdgeGlowTop.onAbsorb(initialVelocity);
|
||||
if (shouldAbsorb(mEdgeGlowTop, initialVelocity)) {
|
||||
mEdgeGlowTop.onAbsorb(initialVelocity);
|
||||
} else {
|
||||
if (mFlingRunnable == null) {
|
||||
mFlingRunnable = new FlingRunnable();
|
||||
}
|
||||
mFlingRunnable.start(-initialVelocity);
|
||||
}
|
||||
} else if (flingVelocity && !mEdgeGlowBottom.isFinished()) {
|
||||
mEdgeGlowBottom.onAbsorb(-initialVelocity);
|
||||
if (shouldAbsorb(mEdgeGlowBottom, -initialVelocity)) {
|
||||
mEdgeGlowBottom.onAbsorb(-initialVelocity);
|
||||
} else {
|
||||
if (mFlingRunnable == null) {
|
||||
mFlingRunnable = new FlingRunnable();
|
||||
}
|
||||
mFlingRunnable.start(-initialVelocity);
|
||||
}
|
||||
} else if (flingVelocity
|
||||
&& !((mFirstPosition == 0
|
||||
&& firstChildTop == contentTop - mOverscrollDistance)
|
||||
@@ -4301,6 +4323,60 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if edgeEffect should call onAbsorb() with veclocity or false if it should
|
||||
* animate with a fling. It will animate with a fling if the velocity will remove the
|
||||
* EdgeEffect through its normal operation.
|
||||
*
|
||||
* @param edgeEffect The EdgeEffect that might absorb the velocity.
|
||||
* @param velocity The velocity of the fling motion
|
||||
* @return true if the velocity should be absorbed or false if it should be flung.
|
||||
*/
|
||||
private boolean shouldAbsorb(EdgeEffect edgeEffect, int velocity) {
|
||||
if (velocity > 0) {
|
||||
return true;
|
||||
}
|
||||
float distance = edgeEffect.getDistance() * getHeight();
|
||||
|
||||
// This is flinging without the spring, so let's see if it will fling past the overscroll
|
||||
if (mFlingRunnable == null) {
|
||||
mFlingRunnable = new FlingRunnable();
|
||||
}
|
||||
float flingDistance = mFlingRunnable.getSplineFlingDistance(-velocity);
|
||||
|
||||
return flingDistance < distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by consumeFlingInHorizontalStretch() and consumeFlinInVerticalStretch() for
|
||||
* consuming deltas from EdgeEffects
|
||||
* @param unconsumed The unconsumed delta that the EdgeEffets may consume
|
||||
* @return The unconsumed delta after the EdgeEffects have had an opportunity to consume.
|
||||
*/
|
||||
private int consumeFlingInStretch(int unconsumed) {
|
||||
if (unconsumed < 0 && mEdgeGlowTop != null && mEdgeGlowTop.getDistance() != 0f) {
|
||||
int size = getHeight();
|
||||
float deltaDistance = unconsumed * FLING_DESTRETCH_FACTOR / size;
|
||||
int consumed = Math.round(size / FLING_DESTRETCH_FACTOR
|
||||
* mEdgeGlowTop.onPullDistance(deltaDistance, 0.5f));
|
||||
if (consumed != unconsumed) {
|
||||
mEdgeGlowTop.finish();
|
||||
}
|
||||
return unconsumed - consumed;
|
||||
}
|
||||
if (unconsumed > 0 && mEdgeGlowBottom != null && mEdgeGlowBottom.getDistance() != 0f) {
|
||||
int size = getHeight();
|
||||
float deltaDistance = -unconsumed * FLING_DESTRETCH_FACTOR / size;
|
||||
int consumed = Math.round(-size / FLING_DESTRETCH_FACTOR
|
||||
* mEdgeGlowBottom.onPullDistance(deltaDistance, 0.5f));
|
||||
if (consumed != unconsumed) {
|
||||
mEdgeGlowBottom.finish();
|
||||
}
|
||||
return unconsumed - consumed;
|
||||
}
|
||||
return unconsumed;
|
||||
}
|
||||
|
||||
private boolean shouldDisplayEdgeEffects() {
|
||||
return getOverScrollMode() != OVER_SCROLL_NEVER;
|
||||
}
|
||||
@@ -4783,6 +4859,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
mScroller = new OverScroller(getContext());
|
||||
}
|
||||
|
||||
float getSplineFlingDistance(int velocity) {
|
||||
return (float) mScroller.getSplineFlingDistance(velocity);
|
||||
}
|
||||
|
||||
// Use AbsListView#fling(int) instead
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
|
||||
void start(int initialVelocity) {
|
||||
@@ -4905,6 +4985,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
}
|
||||
|
||||
if (mItemCount == 0 || getChildCount() == 0) {
|
||||
mEdgeGlowBottom.onRelease();
|
||||
mEdgeGlowTop.onRelease();
|
||||
endFling();
|
||||
return;
|
||||
}
|
||||
@@ -4915,7 +4997,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
|
||||
// Flip sign to convert finger direction to list items direction
|
||||
// (e.g. finger moving down means list is moving towards the top)
|
||||
int delta = mLastFlingY - y;
|
||||
int delta = consumeFlingInStretch(mLastFlingY - y);
|
||||
|
||||
// Pretend that each frame of a fling scroll is a touch scroll
|
||||
if (delta > 0) {
|
||||
|
||||
@@ -77,6 +77,14 @@ public class HorizontalScrollView extends FrameLayout {
|
||||
|
||||
private static final String TAG = "HorizontalScrollView";
|
||||
|
||||
/**
|
||||
* When flinging the stretch towards scrolling content, it should destretch quicker than the
|
||||
* fling would normally do. The visual effect of flinging the stretch looks strange as little
|
||||
* appears to happen at first and then when the stretch disappears, the content starts
|
||||
* scrolling quickly.
|
||||
*/
|
||||
private static final float FLING_DESTRETCH_FACTOR = 4f;
|
||||
|
||||
private long mLastScroll;
|
||||
|
||||
private final Rect mTempRect = new Rect();
|
||||
@@ -1456,18 +1464,19 @@ public class HorizontalScrollView extends FrameLayout {
|
||||
int oldY = mScrollY;
|
||||
int x = mScroller.getCurrX();
|
||||
int y = mScroller.getCurrY();
|
||||
int deltaX = consumeFlingInStretch(x - oldX);
|
||||
|
||||
if (oldX != x || oldY != y) {
|
||||
if (deltaX != 0 || oldY != y) {
|
||||
final int range = getScrollRange();
|
||||
final int overscrollMode = getOverScrollMode();
|
||||
final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
|
||||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
|
||||
|
||||
overScrollBy(x - oldX, y - oldY, oldX, oldY, range, 0,
|
||||
overScrollBy(deltaX, y - oldY, oldX, oldY, range, 0,
|
||||
mOverflingDistance, 0, false);
|
||||
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
|
||||
|
||||
if (canOverscroll) {
|
||||
if (canOverscroll && deltaX != 0) {
|
||||
if (x < 0 && oldX >= 0) {
|
||||
mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
|
||||
} else if (x > range && oldX <= range) {
|
||||
@@ -1482,6 +1491,36 @@ public class HorizontalScrollView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by consumeFlingInHorizontalStretch() and consumeFlinInVerticalStretch() for
|
||||
* consuming deltas from EdgeEffects
|
||||
* @param unconsumed The unconsumed delta that the EdgeEffets may consume
|
||||
* @return The unconsumed delta after the EdgeEffects have had an opportunity to consume.
|
||||
*/
|
||||
private int consumeFlingInStretch(int unconsumed) {
|
||||
if (unconsumed > 0 && mEdgeGlowLeft != null && mEdgeGlowLeft.getDistance() != 0f) {
|
||||
int size = getWidth();
|
||||
float deltaDistance = -unconsumed * FLING_DESTRETCH_FACTOR / size;
|
||||
int consumed = Math.round(-size / FLING_DESTRETCH_FACTOR
|
||||
* mEdgeGlowLeft.onPullDistance(deltaDistance, 0.5f));
|
||||
if (consumed != unconsumed) {
|
||||
mEdgeGlowLeft.finish();
|
||||
}
|
||||
return unconsumed - consumed;
|
||||
}
|
||||
if (unconsumed < 0 && mEdgeGlowRight != null && mEdgeGlowRight.getDistance() != 0f) {
|
||||
int size = getWidth();
|
||||
float deltaDistance = unconsumed * FLING_DESTRETCH_FACTOR / size;
|
||||
int consumed = Math.round(size / FLING_DESTRETCH_FACTOR
|
||||
* mEdgeGlowRight.onPullDistance(deltaDistance, 0.5f));
|
||||
if (consumed != unconsumed) {
|
||||
mEdgeGlowRight.finish();
|
||||
}
|
||||
return unconsumed - consumed;
|
||||
}
|
||||
return unconsumed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the view to the given child.
|
||||
*
|
||||
@@ -1746,11 +1785,23 @@ public class HorizontalScrollView extends FrameLayout {
|
||||
|
||||
int maxScroll = Math.max(0, right - width);
|
||||
|
||||
boolean shouldFling = false;
|
||||
if (mScrollX == 0 && !mEdgeGlowLeft.isFinished()) {
|
||||
mEdgeGlowLeft.onAbsorb(-velocityX);
|
||||
if (shouldAbsorb(mEdgeGlowLeft, -velocityX)) {
|
||||
mEdgeGlowLeft.onAbsorb(-velocityX);
|
||||
} else {
|
||||
shouldFling = true;
|
||||
}
|
||||
} else if (mScrollX == maxScroll && !mEdgeGlowRight.isFinished()) {
|
||||
mEdgeGlowRight.onAbsorb(velocityX);
|
||||
if (shouldAbsorb(mEdgeGlowRight, velocityX)) {
|
||||
mEdgeGlowRight.onAbsorb(velocityX);
|
||||
} else {
|
||||
shouldFling = true;
|
||||
}
|
||||
} else {
|
||||
shouldFling = true;
|
||||
}
|
||||
if (shouldFling) {
|
||||
mScroller.fling(mScrollX, mScrollY, velocityX, 0, 0,
|
||||
maxScroll, 0, 0, width / 2, 0);
|
||||
|
||||
@@ -1773,6 +1824,27 @@ public class HorizontalScrollView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if edgeEffect should call onAbsorb() with veclocity or false if it should
|
||||
* animate with a fling. It will animate with a fling if the velocity will remove the
|
||||
* EdgeEffect through its normal operation.
|
||||
*
|
||||
* @param edgeEffect The EdgeEffect that might absorb the velocity.
|
||||
* @param velocity The velocity of the fling motion
|
||||
* @return true if the velocity should be absorbed or false if it should be flung.
|
||||
*/
|
||||
private boolean shouldAbsorb(EdgeEffect edgeEffect, int velocity) {
|
||||
if (velocity > 0) {
|
||||
return true;
|
||||
}
|
||||
float distance = edgeEffect.getDistance() * getWidth();
|
||||
|
||||
// This is flinging without the spring, so let's see if it will fling past the overscroll
|
||||
float flingDistance = (float) mScroller.getSplineFlingDistance(-velocity);
|
||||
|
||||
return flingDistance < distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
||||
@@ -527,6 +527,10 @@ public class OverScroller {
|
||||
Math.signum(yvel) == Math.signum(dy);
|
||||
}
|
||||
|
||||
double getSplineFlingDistance(int velocity) {
|
||||
return mScrollerY.getSplineFlingDistance(velocity);
|
||||
}
|
||||
|
||||
static class SplineOverScroller {
|
||||
// Initial position
|
||||
private int mStart;
|
||||
|
||||
@@ -85,6 +85,14 @@ public class ScrollView extends FrameLayout {
|
||||
|
||||
private static final String TAG = "ScrollView";
|
||||
|
||||
/**
|
||||
* When flinging the stretch towards scrolling content, it should destretch quicker than the
|
||||
* fling would normally do. The visual effect of flinging the stretch looks strange as little
|
||||
* appears to happen at first and then when the stretch disappears, the content starts
|
||||
* scrolling quickly.
|
||||
*/
|
||||
private static final float FLING_DESTRETCH_FACTOR = 4f;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
private long mLastScroll;
|
||||
|
||||
@@ -1488,18 +1496,19 @@ public class ScrollView extends FrameLayout {
|
||||
int oldY = mScrollY;
|
||||
int x = mScroller.getCurrX();
|
||||
int y = mScroller.getCurrY();
|
||||
int deltaY = consumeFlingInStretch(y - oldY);
|
||||
|
||||
if (oldX != x || oldY != y) {
|
||||
if (oldX != x || deltaY != 0) {
|
||||
final int range = getScrollRange();
|
||||
final int overscrollMode = getOverScrollMode();
|
||||
final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
|
||||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
|
||||
|
||||
overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, range,
|
||||
overScrollBy(x - oldX, deltaY, oldX, oldY, 0, range,
|
||||
0, mOverflingDistance, false);
|
||||
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
|
||||
|
||||
if (canOverscroll) {
|
||||
if (canOverscroll && deltaY != 0) {
|
||||
if (y < 0 && oldY >= 0) {
|
||||
mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
|
||||
} else if (y > range && oldY <= range) {
|
||||
@@ -1520,6 +1529,36 @@ public class ScrollView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by consumeFlingInHorizontalStretch() and consumeFlinInVerticalStretch() for
|
||||
* consuming deltas from EdgeEffects
|
||||
* @param unconsumed The unconsumed delta that the EdgeEffets may consume
|
||||
* @return The unconsumed delta after the EdgeEffects have had an opportunity to consume.
|
||||
*/
|
||||
private int consumeFlingInStretch(int unconsumed) {
|
||||
if (unconsumed > 0 && mEdgeGlowTop != null && mEdgeGlowTop.getDistance() != 0f) {
|
||||
int size = getHeight();
|
||||
float deltaDistance = -unconsumed * FLING_DESTRETCH_FACTOR / size;
|
||||
int consumed = Math.round(-size / FLING_DESTRETCH_FACTOR
|
||||
* mEdgeGlowTop.onPullDistance(deltaDistance, 0.5f));
|
||||
if (consumed != unconsumed) {
|
||||
mEdgeGlowTop.finish();
|
||||
}
|
||||
return unconsumed - consumed;
|
||||
}
|
||||
if (unconsumed < 0 && mEdgeGlowBottom != null && mEdgeGlowBottom.getDistance() != 0f) {
|
||||
int size = getHeight();
|
||||
float deltaDistance = unconsumed * FLING_DESTRETCH_FACTOR / size;
|
||||
int consumed = Math.round(size / FLING_DESTRETCH_FACTOR
|
||||
* mEdgeGlowBottom.onPullDistance(deltaDistance, 0.5f));
|
||||
if (consumed != unconsumed) {
|
||||
mEdgeGlowBottom.finish();
|
||||
}
|
||||
return unconsumed - consumed;
|
||||
}
|
||||
return unconsumed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the view to the given child.
|
||||
*
|
||||
@@ -1803,14 +1842,43 @@ public class ScrollView extends FrameLayout {
|
||||
fling(velocityY);
|
||||
} else if (!consumed) {
|
||||
if (!mEdgeGlowTop.isFinished()) {
|
||||
mEdgeGlowTop.onAbsorb(-velocityY);
|
||||
if (shouldAbsorb(mEdgeGlowTop, -velocityY)) {
|
||||
mEdgeGlowTop.onAbsorb(-velocityY);
|
||||
} else {
|
||||
fling(velocityY);
|
||||
}
|
||||
} else if (!mEdgeGlowBottom.isFinished()) {
|
||||
mEdgeGlowBottom.onAbsorb(velocityY);
|
||||
if (shouldAbsorb(mEdgeGlowBottom, velocityY)) {
|
||||
mEdgeGlowBottom.onAbsorb(velocityY);
|
||||
} else {
|
||||
fling(velocityY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if edgeEffect should call onAbsorb() with veclocity or false if it should
|
||||
* animate with a fling. It will animate with a fling if the velocity will remove the
|
||||
* EdgeEffect through its normal operation.
|
||||
*
|
||||
* @param edgeEffect The EdgeEffect that might absorb the velocity.
|
||||
* @param velocity The velocity of the fling motion
|
||||
* @return true if the velocity should be absorbed or false if it should be flung.
|
||||
*/
|
||||
private boolean shouldAbsorb(EdgeEffect edgeEffect, int velocity) {
|
||||
if (velocity > 0) {
|
||||
return true;
|
||||
}
|
||||
float distance = edgeEffect.getDistance() * getHeight();
|
||||
|
||||
// This is flinging without the spring, so let's see if it will fling past the overscroll
|
||||
float flingDistance = (float) mScroller.getSplineFlingDistance(-velocity);
|
||||
|
||||
return flingDistance < distance;
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
private void endDrag() {
|
||||
mIsBeingDragged = false;
|
||||
|
||||
Reference in New Issue
Block a user