summaryrefslogtreecommitdiff
path: root/core/java/android/widget/ScrollView.java
diff options
context:
space:
mode:
authorAdam Powell <adamp@google.com>2010-08-25 14:37:03 -0700
committerAdam Powell <adamp@google.com>2010-11-23 09:30:10 -0800
commit637d337b58d8eec6de19230a5dd5ca5581c0478d (patch)
tree763894435a1f31800765809f49caf4468090755b /core/java/android/widget/ScrollView.java
parente56764a8fd1cb45298d61a26e844d75ae4449f93 (diff)
Merge overscrolling from gingerbread.
Change-Id: I3eb4413261b38ddd8c609d6153bdfd4ae46c6a0f
Diffstat (limited to 'core/java/android/widget/ScrollView.java')
-rw-r--r--core/java/android/widget/ScrollView.java184
1 files changed, 166 insertions, 18 deletions
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index eb527eb3d3e0..3faec5578d66 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -19,8 +19,11 @@ package android.widget;
import com.android.internal.R;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Canvas;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.FocusFinder;
import android.view.KeyEvent;
@@ -61,7 +64,9 @@ public class ScrollView extends FrameLayout {
private long mLastScroll;
private final Rect mTempRect = new Rect();
- private Scroller mScroller;
+ private OverScroller mScroller;
+ private EdgeGlow mEdgeGlowTop;
+ private EdgeGlow mEdgeGlowBottom;
/**
* Flag to indicate that we are moving focus ourselves. This is so the
@@ -115,6 +120,9 @@ public class ScrollView extends FrameLayout {
private int mMinimumVelocity;
private int mMaximumVelocity;
+ private int mOverscrollDistance;
+ private int mOverflingDistance;
+
/**
* ID of the active pointer. This is used to retain consistency during
* drags/flings if multiple pointers are used.
@@ -187,7 +195,7 @@ public class ScrollView extends FrameLayout {
private void initScrollView() {
- mScroller = new Scroller(getContext());
+ mScroller = new OverScroller(getContext());
setFocusable(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setWillNotDraw(false);
@@ -195,6 +203,8 @@ public class ScrollView extends FrameLayout {
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mOverscrollDistance = configuration.getScaledOverscrollDistance();
+ mOverflingDistance = configuration.getScaledOverflingDistance();
}
@Override
@@ -459,6 +469,9 @@ public class ScrollView extends FrameLayout {
/* Release the drag */
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
+ if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
+ invalidate();
+ }
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
@@ -491,9 +504,7 @@ public class ScrollView extends FrameLayout {
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float y = ev.getY();
- if (!(mIsBeingDragged = inChild((int) ev.getX(), (int) y))) {
- return false;
- }
+ mIsBeingDragged = true;
/*
* If being flinged and user touches, stop the fling. isFinished
@@ -516,7 +527,36 @@ public class ScrollView extends FrameLayout {
final int deltaY = (int) (mLastMotionY - y);
mLastMotionY = y;
- scrollBy(0, deltaY);
+ final int oldX = mScrollX;
+ final int oldY = mScrollY;
+ final int range = getScrollRange();
+ if (overScrollBy(0, deltaY, 0, mScrollY, 0, range,
+ 0, mOverscrollDistance, true)) {
+ // Break our velocity if we hit a scroll barrier.
+ mVelocityTracker.clear();
+ }
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
+ final int pulledToY = oldY + deltaY;
+ if (pulledToY < 0) {
+ mEdgeGlowTop.onPull((float) deltaY / getHeight());
+ if (!mEdgeGlowBottom.isFinished()) {
+ mEdgeGlowBottom.onRelease();
+ }
+ } else if (pulledToY > range) {
+ mEdgeGlowBottom.onPull((float) deltaY / getHeight());
+ if (!mEdgeGlowTop.isFinished()) {
+ mEdgeGlowTop.onRelease();
+ }
+ }
+ if (mEdgeGlowTop != null
+ && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
+ invalidate();
+ }
+ }
}
break;
case MotionEvent.ACTION_UP:
@@ -525,8 +565,15 @@ public class ScrollView extends FrameLayout {
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
- if (getChildCount() > 0 && Math.abs(initialVelocity) > mMinimumVelocity) {
- fling(-initialVelocity);
+ if (getChildCount() > 0) {
+ if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+ fling(-initialVelocity);
+ } else {
+ final int bottom = getScrollRange();
+ if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, bottom)) {
+ invalidate();
+ }
+ }
}
mActivePointerId = INVALID_POINTER;
@@ -536,16 +583,27 @@ public class ScrollView extends FrameLayout {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
+ if (mEdgeGlowTop != null) {
+ mEdgeGlowTop.onRelease();
+ mEdgeGlowBottom.onRelease();
+ }
}
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
+ if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
+ invalidate();
+ }
mActivePointerId = INVALID_POINTER;
mIsBeingDragged = false;
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
+ if (mEdgeGlowTop != null) {
+ mEdgeGlowTop.onRelease();
+ mEdgeGlowBottom.onRelease();
+ }
}
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -572,6 +630,32 @@ public class ScrollView extends FrameLayout {
}
}
+ @Override
+ protected void onOverScrolled(int scrollX, int scrollY,
+ boolean clampedX, boolean clampedY) {
+ // Treat animating scrolls differently; see #computeScroll() for why.
+ if (!mScroller.isFinished()) {
+ mScrollX = scrollX;
+ mScrollY = scrollY;
+ if (clampedY) {
+ mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange());
+ }
+ } else {
+ super.scrollTo(scrollX, scrollY);
+ }
+ awakenScrollBars();
+ }
+
+ private int getScrollRange() {
+ int scrollRange = 0;
+ if (getChildCount() > 0) {
+ View child = getChildAt(0);
+ scrollRange = Math.max(0,
+ child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop));
+ }
+ return scrollRange;
+ }
+
/**
* <p>
* Finds the next focusable component that fits in this View's bounds
@@ -948,7 +1032,16 @@ public class ScrollView extends FrameLayout {
return contentHeight;
}
- return getChildAt(0).getBottom();
+ int scrollRange = getChildAt(0).getBottom();
+ final int scrollY = mScrollY;
+ final int overscrollBottom = Math.max(0, scrollRange - contentHeight);
+ if (scrollY < 0) {
+ scrollRange -= scrollY;
+ } else if (scrollY > overscrollBottom) {
+ scrollRange += scrollY - overscrollBottom;
+ }
+
+ return scrollRange;
}
@Override
@@ -1009,14 +1102,20 @@ public class ScrollView extends FrameLayout {
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
- if (getChildCount() > 0) {
- View child = getChildAt(0);
- x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth());
- y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight());
- if (x != oldX || y != oldY) {
- mScrollX = x;
- mScrollY = y;
- onScrollChanged(x, y, oldX, oldY);
+ if (oldX != x || oldY != y) {
+ overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, getScrollRange(),
+ 0, mOverflingDistance, false);
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+
+ final int range = getScrollRange();
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
+ if (y < 0 && oldY >= 0) {
+ mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
+ } else if (y > range && oldY <= range) {
+ mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
+ }
}
}
awakenScrollBars();
@@ -1254,7 +1353,7 @@ public class ScrollView extends FrameLayout {
int bottom = getChildAt(0).getHeight();
mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0,
- Math.max(0, bottom - height));
+ Math.max(0, bottom - height), 0, height/2);
final boolean movingDown = velocityY > 0;
@@ -1292,6 +1391,55 @@ public class ScrollView extends FrameLayout {
}
}
+ @Override
+ public void setOverScrollMode(int mode) {
+ if (mode != OVER_SCROLL_NEVER) {
+ if (mEdgeGlowTop == null) {
+ final Resources res = getContext().getResources();
+ final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
+ final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
+ mEdgeGlowTop = new EdgeGlow(edge, glow);
+ mEdgeGlowBottom = new EdgeGlow(edge, glow);
+ }
+ } else {
+ mEdgeGlowTop = null;
+ mEdgeGlowBottom = null;
+ }
+ super.setOverScrollMode(mode);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ if (mEdgeGlowTop != null) {
+ final int scrollY = mScrollY;
+ if (!mEdgeGlowTop.isFinished()) {
+ final int restoreCount = canvas.save();
+ final int width = getWidth();
+
+ canvas.translate(-width / 2, Math.min(0, scrollY));
+ mEdgeGlowTop.setSize(width * 2, getHeight());
+ if (mEdgeGlowTop.draw(canvas)) {
+ invalidate();
+ }
+ canvas.restoreToCount(restoreCount);
+ }
+ if (!mEdgeGlowBottom.isFinished()) {
+ final int restoreCount = canvas.save();
+ final int width = getWidth();
+ final int height = getHeight();
+
+ canvas.translate(-width / 2, Math.max(getScrollRange(), scrollY) + height);
+ canvas.rotate(180, width, 0);
+ mEdgeGlowBottom.setSize(width * 2, height);
+ if (mEdgeGlowBottom.draw(canvas)) {
+ invalidate();
+ }
+ canvas.restoreToCount(restoreCount);
+ }
+ }
+ }
+
private int clamp(int n, int my, int child) {
if (my >= child || n < 0) {
/* my >= child is this case: