diff options
| author | Adam Powell <adamp@google.com> | 2010-08-25 14:37:03 -0700 |
|---|---|---|
| committer | Adam Powell <adamp@google.com> | 2010-11-23 09:30:10 -0800 |
| commit | 637d337b58d8eec6de19230a5dd5ca5581c0478d (patch) | |
| tree | 763894435a1f31800765809f49caf4468090755b /core/java/android/widget/ScrollView.java | |
| parent | e56764a8fd1cb45298d61a26e844d75ae4449f93 (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.java | 184 |
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: |
