From 9d32d24dbd8a015c9d5c44ed4901d5a666eb8e7f Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Mon, 29 Mar 2010 16:02:07 -0700 Subject: [PATCH] Remove overscrolling Change-Id: I7e9db8d8a9b8ef67f0c0c82bf57c9155b7ebabea --- api/current.xml | 543 +------------- core/java/android/view/View.java | 192 ----- core/java/android/webkit/WebSettings.java | 18 - core/java/android/webkit/WebView.java | 202 ++---- core/java/android/widget/AbsListView.java | 345 ++------- core/java/android/widget/GridView.java | 7 +- .../android/widget/HorizontalScrollView.java | 101 +-- core/java/android/widget/ListView.java | 156 +--- core/java/android/widget/OverScroller.java | 682 ------------------ core/java/android/widget/ScrollView.java | 81 +-- core/java/android/widget/Scroller.java | 483 +++++-------- core/res/res/layout/alert_dialog.xml | 3 +- .../res/layout/preference_dialog_edittext.xml | 3 +- core/res/res/layout/select_dialog.xml | 3 +- core/res/res/layout/status_bar_expanded.xml | 1 - core/res/res/values/attrs.xml | 17 - core/res/res/values/public.xml | 11 +- 17 files changed, 362 insertions(+), 2486 deletions(-) delete mode 100644 core/java/android/widget/OverScroller.java diff --git a/api/current.xml b/api/current.xml index da47553ee2bac..276fd3263e756 100644 --- a/api/current.xml +++ b/api/current.xml @@ -5960,39 +5960,6 @@ visibility="public" > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - computeHorizontalScrollExtent(); - final boolean canScrollVertical = - computeVerticalScrollRange() > computeVerticalScrollExtent(); - final boolean overscrollHorizontal = overscrollMode == OVERSCROLL_ALWAYS || - (overscrollMode == OVERSCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal); - final boolean overscrollVertical = overscrollMode == OVERSCROLL_ALWAYS || - (overscrollMode == OVERSCROLL_IF_CONTENT_SCROLLS && canScrollVertical); - - int newScrollX = scrollX + deltaX; - if (overscrollHorizontal) { - // Scale the scroll amount if we're in the dropoff zone - final int dropoffX = maxOverscrollX / 2; - final int dropoffLeft = -dropoffX; - final int dropoffRight = dropoffX + scrollRangeX; - if ((scrollX < dropoffLeft && deltaX < 0) || - (scrollX > dropoffRight && deltaX > 0)) { - newScrollX = scrollX + deltaX / 2; - } else { - if (newScrollX > dropoffRight && deltaX > 0) { - int extra = newScrollX - dropoffRight; - newScrollX = dropoffRight + extra / 2; - } else if (newScrollX < dropoffLeft && deltaX < 0) { - int extra = newScrollX - dropoffLeft; - newScrollX = dropoffLeft + extra / 2; - } - } - } else { - maxOverscrollX = 0; - } - - int newScrollY = scrollY + deltaY; - if (overscrollVertical) { - final int dropoffY = maxOverscrollY / 2; - final int dropoffTop = -dropoffY; - final int dropoffBottom = dropoffY + scrollRangeY; - if ((scrollY < dropoffTop && deltaY < 0) || - (scrollY > dropoffBottom && deltaY > 0)) { - newScrollY = scrollY + deltaY / 2; - } else { - if (newScrollY > dropoffBottom && deltaY > 0) { - int extra = newScrollY - dropoffBottom; - newScrollY = dropoffBottom + extra / 2; - } else if (newScrollY < dropoffTop && deltaY < 0) { - int extra = newScrollY - dropoffTop; - newScrollY = dropoffTop + extra / 2; - } - } - } else { - maxOverscrollY = 0; - } - - // Clamp values if at the limits and record - final int left = -maxOverscrollX; - final int right = maxOverscrollX + scrollRangeX; - final int top = -maxOverscrollY; - final int bottom = maxOverscrollY + scrollRangeY; - - boolean clampedX = false; - if (newScrollX > right) { - newScrollX = right; - clampedX = true; - } else if (newScrollX < left) { - newScrollX = left; - clampedX = true; - } - - boolean clampedY = false; - if (newScrollY > bottom) { - newScrollY = bottom; - clampedY = true; - } else if (newScrollY < top) { - newScrollY = top; - clampedY = true; - } - - onOverscrolled(newScrollX, newScrollY, clampedX, clampedY); - - return clampedX || clampedY; - } - - /** - * Called by {@link #overscrollBy(int, int, int, int, int, int, int, int, boolean)} to - * respond to the results of an overscroll operation. - * - * @param scrollX New X scroll value in pixels - * @param scrollY New Y scroll value in pixels - * @param clampedX True if scrollX was clamped to an overscroll boundary - * @param clampedY True if scrollY was clamped to an overscroll boundary - */ - protected void onOverscrolled(int scrollX, int scrollY, - boolean clampedX, boolean clampedY) { - // Intentionally empty. - } - - /** - * Returns the overscroll mode for this view. The result will be - * one of {@link #OVERSCROLL_ALWAYS} (default), {@link #OVERSCROLL_IF_CONTENT_SCROLLS} - * (allow overscrolling only if the view content is larger than the container), - * or {@link #OVERSCROLL_NEVER}. - * - * @return This view's overscroll mode. - */ - public int getOverscrollMode() { - return mOverscrollMode; - } - - /** - * Set the overscroll mode for this view. Valid overscroll modes are - * {@link #OVERSCROLL_ALWAYS} (default), {@link #OVERSCROLL_IF_CONTENT_SCROLLS} - * (allow overscrolling only if the view content is larger than the container), - * or {@link #OVERSCROLL_NEVER}. - * - * Setting the overscroll mode of a view will have an effect only if the - * view is capable of scrolling. - * - * @param overscrollMode The new overscroll mode for this view. - */ - public void setOverscrollMode(int overscrollMode) { - if (overscrollMode != OVERSCROLL_ALWAYS && - overscrollMode != OVERSCROLL_IF_CONTENT_SCROLLS && - overscrollMode != OVERSCROLL_NEVER) { - throw new IllegalArgumentException("Invalid overscroll mode " + overscrollMode); - } - mOverscrollMode = overscrollMode; - } /** * A MeasureSpec encapsulates the layout requirements passed from parent to child. diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 1b801d4d13807..b767f11753dc2 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -207,7 +207,6 @@ public class WebSettings { private boolean mBuiltInZoomControls = false; private boolean mAllowFileAccess = true; private boolean mLoadWithOverviewMode = false; - private boolean mUseWebViewBackgroundOverscrollBackground = true; // private WebSettings, not accessible by the host activity static private int mDoubleTapToastCount = 3; @@ -485,23 +484,6 @@ public class WebSettings { return mLoadWithOverviewMode; } - /** - * Set whether the WebView uses its background for over scroll background. - * If true, it will use the WebView's background. If false, it will use an - * internal pattern. Default is true. - */ - public void setUseWebViewBackgroundForOverscrollBackground(boolean view) { - mUseWebViewBackgroundOverscrollBackground = view; - } - - /** - * Returns true if this WebView uses WebView's background instead of - * internal pattern for over scroll background. - */ - public boolean getUseWebViewBackgroundForOverscrollBackground() { - return mUseWebViewBackgroundOverscrollBackground; - } - /** * Store whether the WebView is saving form data. */ diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 030290ff7de9e..738829577a506 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -37,8 +37,8 @@ import android.graphics.Rect; import android.graphics.Region; import android.graphics.Shader; import android.graphics.drawable.Drawable; -import android.net.http.SslCertificate; import android.net.Uri; +import android.net.http.SslCertificate; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -77,7 +77,7 @@ import android.widget.CheckedTextView; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ListView; -import android.widget.OverScroller; +import android.widget.Scroller; import android.widget.Toast; import android.widget.ZoomButtonsController; import android.widget.ZoomControls; @@ -90,8 +90,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.net.URLDecoder; import java.util.ArrayList; -import java.util.List; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -469,10 +469,7 @@ public class WebView extends AbsoluteLayout // time for the longest scroll animation private static final int MAX_DURATION = 750; // milliseconds private static final int SLIDE_TITLE_DURATION = 500; // milliseconds - private OverScroller mScroller; - private boolean mInOverScrollMode = false; - private static Paint mOverScrollBackground; - private static Paint mOverScrollBorder; + private Scroller mScroller; private boolean mWrapContent; private static final int MOTIONLESS_FALSE = 0; @@ -848,7 +845,7 @@ public class WebView extends AbsoluteLayout mViewManager = new ViewManager(this); mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces); mDatabase = WebViewDatabase.getInstance(context); - mScroller = new OverScroller(context); + mScroller = new Scroller(context); updateMultiTouchSupport(context); } @@ -1051,8 +1048,7 @@ public class WebView extends AbsoluteLayout * Return the amount of the titlebarview (if any) that is visible */ private int getVisibleTitleHeight() { - // need to restrict mScrollY due to over scroll - return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0); + return Math.max(getTitleHeight() - mScrollY, 0); } /* @@ -1662,7 +1658,7 @@ public class WebView extends AbsoluteLayout } nativeClearCursor(); // start next trackball movement from page edge if (bottom) { - return pinScrollTo(mScrollX, computeRealVerticalScrollRange(), true, 0); + return pinScrollTo(mScrollX, computeVerticalScrollRange(), true, 0); } // Page down. int h = getHeight(); @@ -1893,15 +1889,13 @@ public class WebView extends AbsoluteLayout // Expects x in view coordinates private int pinLocX(int x) { - if (mInOverScrollMode) return x; - return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange()); + return pinLoc(x, getViewWidth(), computeHorizontalScrollRange()); } // Expects y in view coordinates private int pinLocY(int y) { - if (mInOverScrollMode) return y; return pinLoc(y, getViewHeightWithTitle(), - computeRealVerticalScrollRange() + getTitleHeight()); + computeVerticalScrollRange() + getTitleHeight()); } /** @@ -2189,7 +2183,7 @@ public class WebView extends AbsoluteLayout // Sets r to be our visible rectangle in content coordinates private void calcOurContentVisibleRect(Rect r) { calcOurVisibleRect(r); - // since we might overscroll, pin the rect to the bounds of the content + // pin the rect to the bounds of the content r.left = Math.max(viewToContentX(r.left), 0); // viewToContentY will remove the total height of the title bar. Add // the visible height back in to account for the fact that if the title @@ -2252,7 +2246,8 @@ public class WebView extends AbsoluteLayout return false; } - private int computeRealHorizontalScrollRange() { + @Override + protected int computeHorizontalScrollRange() { if (mDrawHistory) { return mHistoryWidth; } else { @@ -2262,27 +2257,7 @@ public class WebView extends AbsoluteLayout } @Override - protected int computeHorizontalScrollRange() { - int range = computeRealHorizontalScrollRange(); - - // Adjust reported range if overscrolled to compress the scroll bars - final int scrollX = mScrollX; - final int overscrollRight = computeMaxScrollX(); - if (scrollX < 0) { - range -= scrollX; - } else if (scrollX > overscrollRight) { - range += scrollX - overscrollRight; - } - - return range; - } - - @Override - protected int computeHorizontalScrollOffset() { - return Math.max(mScrollX, 0); - } - - private int computeRealVerticalScrollRange() { + protected int computeVerticalScrollRange() { if (mDrawHistory) { return mHistoryHeight; } else { @@ -2291,22 +2266,6 @@ public class WebView extends AbsoluteLayout } } - @Override - protected int computeVerticalScrollRange() { - int range = computeRealVerticalScrollRange(); - - // Adjust reported range if overscrolled to compress the scroll bars - final int scrollY = mScrollY; - final int overscrollBottom = computeMaxScrollY(); - if (scrollY < 0) { - range -= scrollY; - } else if (scrollY > overscrollBottom) { - range += scrollY - overscrollBottom; - } - - return range; - } - @Override protected int computeVerticalScrollOffset() { return Math.max(mScrollY - getTitleHeight(), 0); @@ -2326,23 +2285,6 @@ public class WebView extends AbsoluteLayout scrollBar.draw(canvas); } - @Override - protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX, - boolean clampedY) { - mInOverScrollMode = false; - int maxX = computeMaxScrollX(); - if (maxX == 0) { - // do not over scroll x if the page just fits the screen - scrollX = pinLocX(scrollX); - } else if (scrollX < 0 || scrollX > maxX) { - mInOverScrollMode = true; - } - if (scrollY < 0 || scrollY > computeMaxScrollY()) { - mInOverScrollMode = true; - } - super.scrollTo(scrollX, scrollY); - } - /** * Get the url for the current page. This is not always the same as the url * passed to WebViewClient.onPageStarted because although the load for @@ -2688,14 +2630,13 @@ public class WebView extends AbsoluteLayout if (mScroller.computeScrollOffset()) { int oldX = mScrollX; int oldY = mScrollY; - int x = mScroller.getCurrX(); - int y = mScroller.getCurrY(); + mScrollX = mScroller.getCurrX(); + mScrollY = mScroller.getCurrY(); postInvalidate(); // So we draw again - if (oldX != x || oldY != y) { - overscrollBy(x - oldX, y - oldY, oldX, oldY, - computeMaxScrollX(), computeMaxScrollY(), - getViewWidth() / 3, getViewHeight() / 3, false); - onScrollChanged(mScrollX, mScrollY, oldX, oldY); + if (oldX != mScrollX || oldY != mScrollY) { + // As onScrollChanged() is not called, sendOurVisibleRect() + // needs to be called explicitly. + sendOurVisibleRect(); } } else { super.computeScroll(); @@ -3106,13 +3047,8 @@ public class WebView extends AbsoluteLayout protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (child == mTitleBar) { // When drawing the title bar, move it horizontally to always show - // at the top of the WebView. While overscroll, stick the title bar - // on the top otherwise we may have two during loading, one is drawn - // here, another is drawn by the Browser. + // at the top of the WebView. mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft()); - if (mScrollY <= 0) { - mTitleBar.offsetTopAndBottom(mScrollY - mTitleBar.getTop()); - } } return super.drawChild(canvas, child, drawingTime); } @@ -3148,36 +3084,6 @@ public class WebView extends AbsoluteLayout } int saveCount = canvas.save(); - if (mInOverScrollMode && !getSettings() - .getUseWebViewBackgroundForOverscrollBackground()) { - if (mOverScrollBackground == null) { - mOverScrollBackground = new Paint(); - Bitmap bm = BitmapFactory.decodeResource( - mContext.getResources(), - com.android.internal.R.drawable.status_bar_background); - mOverScrollBackground.setShader(new BitmapShader(bm, - Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)); - mOverScrollBorder = new Paint(); - mOverScrollBorder.setStyle(Paint.Style.STROKE); - mOverScrollBorder.setStrokeWidth(0); - mOverScrollBorder.setColor(0xffbbbbbb); - } - - int top = getTitleHeight(); - int right = computeRealHorizontalScrollRange(); - int bottom = top + computeRealVerticalScrollRange(); - // first draw the background and anchor to the top of the view - canvas.save(); - canvas.translate(mScrollX, mScrollY); - canvas.clipRect(-mScrollX, top - mScrollY, right - mScrollX, bottom - - mScrollY, Region.Op.DIFFERENCE); - canvas.drawPaint(mOverScrollBackground); - canvas.restore(); - // then draw the border - canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder); - // next clip the region for the content - canvas.clipRect(0, top, right, bottom); - } if (mTitleBar != null) { canvas.translate(0, (int) mTitleBar.getHeight()); } @@ -3195,12 +3101,12 @@ public class WebView extends AbsoluteLayout canvas.restoreToCount(saveCount); // Now draw the shadow. - int titleH = getVisibleTitleHeight(); - if (mTitleBar != null && titleH == 0) { + if (mTitleBar != null) { + int y = mScrollY + getVisibleTitleHeight(); int height = (int) (5f * getContext().getResources() .getDisplayMetrics().density); - mTitleShadow.setBounds(mScrollX, mScrollY, mScrollX + getWidth(), - mScrollY + height); + mTitleShadow.setBounds(mScrollX, y, mScrollX + getWidth(), + y + height); mTitleShadow.draw(canvas); } if (AUTO_REDRAW_HACK && mAutoRedraw) { @@ -4232,14 +4138,12 @@ public class WebView extends AbsoluteLayout @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - if (!mInOverScrollMode) { - sendOurVisibleRect(); - // update WebKit if visible title bar height changed. The logic is same - // as getVisibleTitleHeight. - int titleHeight = getTitleHeight(); - if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) { - sendViewSizeZoom(); - } + sendOurVisibleRect(); + // update WebKit if visible title bar height changed. The logic is same + // as getVisibleTitleHeight. + int titleHeight = getTitleHeight(); + if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) { + sendViewSizeZoom(); } } @@ -4309,7 +4213,7 @@ public class WebView extends AbsoluteLayout public DragTrackerHandler(float x, float y, DragTracker proxy) { mProxy = proxy; - int docBottom = computeRealVerticalScrollRange() + getTitleHeight(); + int docBottom = computeVerticalScrollRange() + getTitleHeight(); int viewTop = getScrollY(); int viewBottom = viewTop + getHeight(); @@ -4322,7 +4226,7 @@ public class WebView extends AbsoluteLayout " up/down= " + mMinDY + " " + mMaxDY); } - int docRight = computeRealHorizontalScrollRange(); + int docRight = computeHorizontalScrollRange(); int viewLeft = getScrollX(); int viewRight = viewLeft + getWidth(); mStartX = x; @@ -4826,6 +4730,18 @@ public class WebView extends AbsoluteLayout } // do pan + int newScrollX = pinLocX(mScrollX + deltaX); + int newDeltaX = newScrollX - mScrollX; + if (deltaX != newDeltaX) { + deltaX = newDeltaX; + fDeltaX = (float) newDeltaX; + } + int newScrollY = pinLocY(mScrollY + deltaY); + int newDeltaY = newScrollY - mScrollY; + if (deltaY != newDeltaY) { + deltaY = newDeltaY; + fDeltaY = (float) newDeltaY; + } boolean done = false; boolean keepScrollBarsVisible = false; if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) { @@ -4985,12 +4901,6 @@ public class WebView extends AbsoluteLayout mVelocityTracker.addMovement(ev); doFling(); break; - } else { - if (mScroller.springback(mScrollX, mScrollY, 0, - computeMaxScrollX(), 0, - computeMaxScrollY())) { - invalidate(); - } } mLastVelocity = 0; WebViewCore.resumePriority(); @@ -5001,8 +4911,6 @@ public class WebView extends AbsoluteLayout } case MotionEvent.ACTION_CANCEL: { if (mTouchMode == TOUCH_DRAG_MODE) { - mScroller.springback(mScrollX, mScrollY, 0, - computeMaxScrollX(), 0, computeMaxScrollY()); invalidate(); } cancelWebCoreTouchEvent(contentX, contentY, false); @@ -5062,9 +4970,7 @@ public class WebView extends AbsoluteLayout private void doDrag(int deltaX, int deltaY) { if ((deltaX | deltaY) != 0) { - overscrollBy(deltaX, deltaY, mScrollX, mScrollY, - computeMaxScrollX(), computeMaxScrollY(), - getViewWidth() / 3, getViewHeight() / 3, true); + scrollBy(deltaX, deltaY); } if (!getSettings().getBuiltInZoomControls()) { boolean showPlusMinus = mMinZoomScale < mMaxZoomScale; @@ -5401,17 +5307,17 @@ public class WebView extends AbsoluteLayout } private int computeMaxScrollX() { - return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0); + return Math.max(computeHorizontalScrollRange() - getViewWidth(), 0); } private int computeMaxScrollY() { - return Math.max(computeRealVerticalScrollRange() + getTitleHeight() + return Math.max(computeVerticalScrollRange() + getTitleHeight() - getViewHeightWithTitle(), 0); } public void flingScroll(int vx, int vy) { mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0, - computeMaxScrollY(), getViewWidth() / 3, getViewHeight() / 3); + computeMaxScrollY()); invalidate(); } @@ -5440,10 +5346,6 @@ public class WebView extends AbsoluteLayout } if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) { WebViewCore.resumePriority(); - if (mScroller.springback(mScrollX, mScrollY, 0, computeMaxScrollX(), - 0, computeMaxScrollY())) { - invalidate(); - } return; } float currentVelocity = mScroller.getCurrVelocity(); @@ -5471,9 +5373,7 @@ public class WebView extends AbsoluteLayout mLastVelY = vy; mLastVelocity = (float) Math.hypot(vx, vy); - // no horizontal overscroll if the content just fits - mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY, - maxX == 0 ? 0 : getViewWidth() / 3, getViewHeight() / 3); + mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY); // TODO: duration is calculated based on velocity, if the range is // small, the animation will stop before duration is up. We may // want to calculate how long the animation is going to run to precisely @@ -6523,10 +6423,6 @@ public class WebView extends AbsoluteLayout case MotionEvent.ACTION_CANCEL: if (mDeferTouchMode == TOUCH_DRAG_MODE) { // no fling in defer process - mScroller.springback(mScrollX, mScrollY, 0, - computeMaxScrollX(), 0, - computeMaxScrollY()); - invalidate(); WebViewCore.resumePriority(); } mDeferTouchMode = TOUCH_DONE_MODE; diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index bd6105f194bd9..d615fd026912e 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -127,17 +127,6 @@ public abstract class AbsListView extends AdapterView implements Te */ static final int TOUCH_MODE_FLING = 4; - /** - * Indicates the touch gesture is an overscroll - a scroll beyond the beginning or end. - */ - static final int TOUCH_MODE_OVERSCROLL = 5; - - /** - * Indicates the view is being flung outside of normal content bounds - * and will spring back. - */ - static final int TOUCH_MODE_OVERFLING = 6; - /** * Regular layout - usually an unsolicited layout from the view system */ @@ -379,16 +368,6 @@ public abstract class AbsListView extends AdapterView implements Te private ContextMenuInfo mContextMenuInfo = null; - /** - * Maximum distance to overscroll by - */ - int mOverscrollMax; - - /** - * Content height divided by this is the overscroll limit. - */ - static final int OVERSCROLL_LIMIT_DIVISOR = 3; - /** * Used to request a layout when we changed touch mode */ @@ -1095,10 +1074,6 @@ public abstract class AbsListView extends AdapterView implements Te int result; if (mSmoothScrollbarEnabled) { result = Math.max(mItemCount * 100, 0); - if (mScrollY != 0) { - // Compensate for overscroll - result += Math.abs((int) ((float) mScrollY / getHeight() * mItemCount * 100)); - } } else { result = mItemCount; } @@ -1171,8 +1146,6 @@ public abstract class AbsListView extends AdapterView implements Te layoutChildren(); mInLayout = false; - - mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR; } /** @@ -1947,10 +1920,9 @@ public abstract class AbsListView extends AdapterView implements Te // Check if we have moved far enough that it looks more like a // scroll than a tap final int distance = Math.abs(deltaY); - final boolean overscroll = mScrollY != 0; - if (overscroll || distance > mTouchSlop) { + if (distance > mTouchSlop) { createScrollingCache(); - mTouchMode = overscroll ? TOUCH_MODE_OVERSCROLL : TOUCH_MODE_SCROLL; + mTouchMode = TOUCH_MODE_SCROLL; mMotionCorrection = deltaY; final Handler handler = getHandler(); // Handler should not be null unless the AbsListView is not attached to a @@ -1986,18 +1958,6 @@ public abstract class AbsListView extends AdapterView implements Te // touch mode). Force an initial layout to get rid of the selection. layoutChildren(); } - } else { - int touchMode = mTouchMode; - if (touchMode == TOUCH_MODE_OVERSCROLL || touchMode == TOUCH_MODE_OVERFLING) { - if (mFlingRunnable != null) { - mFlingRunnable.endFling(); - } - - if (mScrollY != 0) { - mScrollY = 0; - invalidate(); - } - } } } @@ -2028,63 +1988,49 @@ public abstract class AbsListView extends AdapterView implements Te switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { - switch (mTouchMode) { - case TOUCH_MODE_OVERFLING: { - mFlingRunnable.endFling(); - mTouchMode = TOUCH_MODE_OVERSCROLL; - 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) - && (getAdapter().isEnabled(motionPosition))) { - // User clicked on an actual view (and was not stopping a fling). It might be a - // click or a scroll. Assume it is a click until proven otherwise - mTouchMode = TOUCH_MODE_DOWN; - // FIXME Debounce - if (mPendingCheckForTap == null) { - mPendingCheckForTap = new CheckForTap(); - } - postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); - } else { - if (ev.getEdgeFlags() != 0 && motionPosition < 0) { - // If we couldn't find a view to click on, but the down event was touching - // the edge, we will bail out and try again. This allows the edge correcting - // code in ViewRoot to try to find a nearby view to select - return false; - } - - 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); - } + 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) + && (getAdapter().isEnabled(motionPosition))) { + // User clicked on an actual view (and was not stopping a fling). It might be a + // click or a scroll. Assume it is a click until proven otherwise + mTouchMode = TOUCH_MODE_DOWN; + // FIXME Debounce + if (mPendingCheckForTap == null) { + mPendingCheckForTap = new CheckForTap(); + } + postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); + } else { + if (ev.getEdgeFlags() != 0 && motionPosition < 0) { + // If we couldn't find a view to click on, but the down event was touching + // the edge, we will bail out and try again. This allows the edge correcting + // code in ViewRoot to try to find a nearby view to select + return false; + } + + 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); } } + } - if (motionPosition >= 0) { - // Remember where the motion event started - v = getChildAt(motionPosition - mFirstPosition); - mMotionViewOriginalTop = v.getTop(); - } - mMotionX = x; - mMotionY = y; - mMotionPosition = motionPosition; - mLastY = Integer.MIN_VALUE; - break; - } + if (motionPosition >= 0) { + // Remember where the motion event started + v = getChildAt(motionPosition - mFirstPosition); + mMotionViewOriginalTop = v.getTop(); } + mMotionX = x; + mMotionY = y; + mMotionPosition = motionPosition; + mLastY = Integer.MIN_VALUE; break; } @@ -2131,62 +2077,12 @@ public abstract class AbsListView extends AdapterView implements Te // supposed to be final int motionViewRealTop = motionView.getTop(); if (atEdge) { - // Apply overscroll - - int overscroll = -incrementalDeltaY - - (motionViewRealTop - motionViewPrevTop); - overscrollBy(0, overscroll, 0, mScrollY, 0, 0, - 0, getOverscrollMax(), true); - mTouchMode = TOUCH_MODE_OVERSCROLL; invalidate(); } } mLastY = y; } break; - - case TOUCH_MODE_OVERSCROLL: - if (y != mLastY) { - deltaY -= mMotionCorrection; - int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY; - - final int oldScroll = mScrollY; - final int newScroll = oldScroll - incrementalDeltaY; - - if ((oldScroll >= 0 && newScroll <= 0) || - (oldScroll <= 0 && newScroll >= 0)) { - // Coming back to 'real' list scrolling - incrementalDeltaY = -newScroll; - mScrollY = 0; - - // No need to do all this work if we're not going to move anyway - if (incrementalDeltaY != 0) { - trackMotionScroll(incrementalDeltaY, incrementalDeltaY); - } - - // Check to see if we are back in - View motionView = this.getChildAt(mMotionPosition - mFirstPosition); - if (motionView != null) { - mTouchMode = TOUCH_MODE_SCROLL; - - // We did not scroll the full amount. Treat this essentially like the - // start of a new touch scroll - final int motionPosition = findClosestMotionRow(y); - - mMotionCorrection = 0; - motionView = getChildAt(motionPosition - mFirstPosition); - mMotionViewOriginalTop = motionView.getTop(); - mMotionY = y; - mMotionPosition = motionPosition; - } - } else { - overscrollBy(0, -incrementalDeltaY, 0, mScrollY, 0, 0, - 0, getOverscrollMax(), true); - invalidate(); - } - mLastY = y; - } - break; } break; @@ -2286,23 +2182,6 @@ public abstract class AbsListView extends AdapterView implements Te reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); } break; - - case TOUCH_MODE_OVERSCROLL: - if (mFlingRunnable == null) { - mFlingRunnable = new FlingRunnable(); - } - final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId); - - reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); - if (Math.abs(initialVelocity) > mMinimumVelocity) { - mFlingRunnable.startOverfling(-initialVelocity); - } else { - mFlingRunnable.startSpringback(); - } - - break; } setPressed(false); @@ -2332,36 +2211,22 @@ public abstract class AbsListView extends AdapterView implements Te } case MotionEvent.ACTION_CANCEL: { - switch (mTouchMode) { - case TOUCH_MODE_OVERSCROLL: - if (mFlingRunnable == null) { - mFlingRunnable = new FlingRunnable(); - } - mFlingRunnable.startSpringback(); - break; - - case TOUCH_MODE_OVERFLING: - // Do nothing - let it play out. - break; - - default: - mTouchMode = TOUCH_MODE_REST; - setPressed(false); - View motionView = this.getChildAt(mMotionPosition - mFirstPosition); - if (motionView != null) { - motionView.setPressed(false); - } - clearScrollingCache(); + mTouchMode = TOUCH_MODE_REST; + setPressed(false); + View motionView = this.getChildAt(mMotionPosition - mFirstPosition); + if (motionView != null) { + motionView.setPressed(false); + } + clearScrollingCache(); - final Handler handler = getHandler(); - if (handler != null) { - handler.removeCallbacks(mPendingCheckForLongPress); - } + final Handler handler = getHandler(); + if (handler != null) { + handler.removeCallbacks(mPendingCheckForLongPress); + } - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; } mActivePointerId = INVALID_POINTER; @@ -2386,45 +2251,12 @@ public abstract class AbsListView extends AdapterView implements Te return true; } - - @Override - protected void onOverscrolled(int scrollX, int scrollY, - boolean clampedX, boolean clampedY) { - mScrollY = scrollY; - - if (clampedY) { - // Velocity is broken by hitting the limit; don't start a fling off of this. - if (mVelocityTracker != null) { - mVelocityTracker.clear(); - } - } - awakenScrollBars(); - } - - int getOverscrollMax() { - final int childCount = getChildCount(); - if (childCount > 0) { - return Math.min(mOverscrollMax, - getChildAt(childCount - 1).getBottom() / OVERSCROLL_LIMIT_DIVISOR); - } else { - return mOverscrollMax; - } - } @Override public void draw(Canvas canvas) { super.draw(canvas); if (mFastScroller != null) { - final int scrollY = mScrollY; - if (scrollY != 0) { - // Pin the fast scroll thumb to the top/bottom during overscroll. - int restoreCount = canvas.save(); - canvas.translate(0, (float) scrollY); - mFastScroller.draw(canvas); - canvas.restoreToCount(restoreCount); - } else { - mFastScroller.draw(canvas); - } + mFastScroller.draw(canvas); } } @@ -2443,9 +2275,6 @@ public abstract class AbsListView extends AdapterView implements Te 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(); @@ -2565,7 +2394,7 @@ public abstract class AbsListView extends AdapterView implements Te /** * Tracks the decay of a fling scroll */ - private final OverScroller mScroller; + private final Scroller mScroller; /** * Y value reported by mScroller on the previous fling @@ -2573,7 +2402,7 @@ public abstract class AbsListView extends AdapterView implements Te private int mLastFlingY; FlingRunnable() { - mScroller = new OverScroller(getContext()); + mScroller = new Scroller(getContext()); } void start(int initialVelocity) { @@ -2592,32 +2421,6 @@ public abstract class AbsListView extends AdapterView implements Te } } - void startSpringback() { - if (mScroller.springback(0, mScrollY, 0, 0, 0, 0)) { - mTouchMode = TOUCH_MODE_OVERFLING; - invalidate(); - post(this); - } else { - mTouchMode = TOUCH_MODE_REST; - } - } - - void startOverfling(int initialVelocity) { - final int min = mScrollY > 0 ? Integer.MIN_VALUE : 0; - final int max = mScrollY > 0 ? 0 : Integer.MAX_VALUE; - mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, min, max, 0, getHeight()); - mTouchMode = TOUCH_MODE_OVERFLING; - invalidate(); - post(this); - } - - void edgeReached() { - mScroller.notifyVerticalEdgeReached(mScrollY, 0, getOverscrollMax()); - mTouchMode = TOUCH_MODE_OVERFLING; - invalidate(); - post(this); - } - void startScroll(int distance, int duration) { int initialY = distance < 0 ? Integer.MAX_VALUE : 0; mLastFlingY = initialY; @@ -2650,7 +2453,7 @@ public abstract class AbsListView extends AdapterView implements Te return; } - final OverScroller scroller = mScroller; + final Scroller scroller = mScroller; boolean more = scroller.computeScrollOffset(); final int y = scroller.getCurrY(); @@ -2685,16 +2488,8 @@ public abstract class AbsListView extends AdapterView implements Te if (motionView != null) { oldTop = motionView.getTop(); } - if (trackMotionScroll(delta, delta)) { - if (motionView != null) { - // Tweak the scroll for how far we overshot - int overshoot = -(delta - (motionView.getTop() - oldTop)); - overscrollBy(0, overshoot, 0, mScrollY, 0, 0, - 0, getOverscrollMax(), false); - } - edgeReached(); - break; - } + + trackMotionScroll(delta, delta); if (more) { invalidate(); @@ -2712,24 +2507,6 @@ public abstract class AbsListView extends AdapterView implements Te } break; } - - case TOUCH_MODE_OVERFLING: { - final OverScroller scroller = mScroller; - if (scroller.computeScrollOffset()) { - final int scrollY = mScrollY; - final int deltaY = scroller.getCurrY() - scrollY; - if (overscrollBy(0, deltaY, 0, scrollY, 0, 0, - 0, getOverscrollMax(), false)) { - startSpringback(); - } else { - invalidate(); - post(this); - } - } else { - endFling(); - } - break; - } } } diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index d8679802a42d4..d2829db0de185 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -1873,12 +1873,7 @@ public class GridView extends AbsListView { // TODO: Account for vertical spacing too final int numColumns = mNumColumns; final int rowCount = (mItemCount + numColumns - 1) / numColumns; - int result = Math.max(rowCount * 100, 0); - if (mScrollY != 0) { - // Compensate for overscroll - result += Math.abs((int) ((float) mScrollY / getHeight() * rowCount * 100)); - } - return result; + return Math.max(rowCount * 100, 0); } } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 3bd47679edd5f..20c2fe444a6d9 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -16,19 +16,19 @@ package android.widget; -import android.util.AttributeSet; -import android.graphics.Rect; -import android.view.View; -import android.view.VelocityTracker; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.KeyEvent; -import android.view.FocusFinder; -import android.view.MotionEvent; -import android.view.ViewParent; -import android.view.animation.AnimationUtils; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.FocusFinder; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.animation.AnimationUtils; import java.util.List; @@ -63,7 +63,7 @@ public class HorizontalScrollView extends FrameLayout { private long mLastScroll; private final Rect mTempRect = new Rect(); - private OverScroller mScroller; + private Scroller mScroller; /** * Flag to indicate that we are moving focus ourselves. This is so the @@ -189,7 +189,7 @@ public class HorizontalScrollView extends FrameLayout { private void initScrollView() { - mScroller = new OverScroller(getContext()); + mScroller = new Scroller(getContext()); setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); @@ -456,9 +456,6 @@ public class HorizontalScrollView extends FrameLayout { /* Release the drag */ mIsBeingDragged = false; mActivePointerId = INVALID_POINTER; - if (mScroller.springback(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) { - invalidate(); - } break; case MotionEvent.ACTION_POINTER_UP: onSecondaryPointerUp(ev); @@ -516,11 +513,7 @@ public class HorizontalScrollView extends FrameLayout { final int deltaX = (int) (mLastMotionX - x); mLastMotionX = x; - final int oldX = mScrollX; - final int oldY = mScrollY; - overscrollBy(deltaX, 0, mScrollX, 0, getScrollRange(), 0, - getOverscrollMax(), 0, true); - onScrollChanged(mScrollX, mScrollY, oldX, oldY); + scrollBy(deltaX, 0); } break; case MotionEvent.ACTION_UP: @@ -529,15 +522,8 @@ public class HorizontalScrollView extends FrameLayout { 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, right, 0, 0)) { - invalidate(); - } - } + if (getChildCount() > 0 && Math.abs(initialVelocity) > mMinimumVelocity) { + fling(-initialVelocity); } mActivePointerId = INVALID_POINTER; @@ -551,9 +537,6 @@ public class HorizontalScrollView extends FrameLayout { break; case MotionEvent.ACTION_CANCEL: if (mIsBeingDragged && getChildCount() > 0) { - if (mScroller.springback(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) { - invalidate(); - } mActivePointerId = INVALID_POINTER; mIsBeingDragged = false; if (mVelocityTracker != null) { @@ -586,32 +569,6 @@ public class HorizontalScrollView 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 (clampedX) { - mScroller.springback(mScrollX, mScrollY, 0, getScrollRange(), 0, 0); - } - } else { - super.scrollTo(scrollX, scrollY); - } - awakenScrollBars(); - } - - private int getOverscrollMax() { - int childCount = getChildCount(); - int containerOverscroll = (getWidth() - mPaddingLeft - mPaddingRight) / 3; - if (childCount > 0) { - return Math.min(containerOverscroll, getChildAt(0).getWidth() / 3); - } else { - return containerOverscroll; - } - } - private int getScrollRange() { int scrollRange = 0; if (getChildCount() > 0) { @@ -994,16 +951,7 @@ public class HorizontalScrollView extends FrameLayout { return contentWidth; } - int scrollRange = getChildAt(0).getRight(); - final int scrollX = mScrollX; - final int overscrollRight = Math.max(0, scrollRange - contentWidth); - if (scrollX < 0) { - scrollRange -= scrollX; - } else if (scrollX > overscrollRight) { - scrollRange += scrollX - overscrollRight; - } - - return scrollRange; + return getChildAt(0).getRight(); } @Override @@ -1064,10 +1012,15 @@ public class HorizontalScrollView extends FrameLayout { int x = mScroller.getCurrX(); int y = mScroller.getCurrY(); - if (oldX != x || oldY != y) { - overscrollBy(x - oldX, y - oldY, oldX, oldY, getScrollRange(), 0, - getOverscrollMax(), 0, false); - onScrollChanged(mScrollX, mScrollY, oldX, oldY); + 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); + } } // Keep on drawing until the animation has finished. @@ -1302,7 +1255,7 @@ public class HorizontalScrollView extends FrameLayout { int right = getChildAt(0).getWidth(); mScroller.fling(mScrollX, mScrollY, velocityX, 0, 0, - Math.max(0, right - width), 0, 0, width/2, 0); + Math.max(0, right - width), 0, 0); final boolean movingRight = velocityX > 0; diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 27700fde779ec..892c44af7ffc4 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -118,9 +118,6 @@ public class ListView extends AbsListView { Drawable mDivider; int mDividerHeight; - Drawable mOverscrollHeader; - Drawable mOverscrollFooter; - private boolean mIsCacheColorOpaque; private boolean mDividerIsOpaque; private boolean mClipDivider; @@ -175,16 +172,6 @@ public class ListView extends AbsListView { setDivider(d); } - final Drawable osHeader = a.getDrawable(com.android.internal.R.styleable.ListView_overscrollHeader); - if (osHeader != null) { - setOverscrollHeader(osHeader); - } - - final Drawable osFooter = a.getDrawable(com.android.internal.R.styleable.ListView_overscrollFooter); - if (osFooter != null) { - setOverscrollFooter(osFooter); - } - // Use the height specified, zero being the default final int dividerHeight = a.getDimensionPixelSize( com.android.internal.R.styleable.ListView_dividerHeight, 0); @@ -2955,52 +2942,14 @@ public class ListView extends AbsListView { } super.setCacheColorHint(color); } - - void drawOverscrollHeader(Canvas canvas, Drawable drawable, Rect bounds) { - final int height = drawable.getMinimumHeight(); - - canvas.save(); - canvas.clipRect(bounds); - - final int span = bounds.bottom - bounds.top; - if (span < height) { - bounds.top = bounds.bottom - height; - } - - drawable.setBounds(bounds); - drawable.draw(canvas); - - canvas.restore(); - } - - void drawOverscrollFooter(Canvas canvas, Drawable drawable, Rect bounds) { - final int height = drawable.getMinimumHeight(); - - canvas.save(); - canvas.clipRect(bounds); - - final int span = bounds.bottom - bounds.top; - if (span < height) { - bounds.bottom = bounds.top + height; - } - - drawable.setBounds(bounds); - drawable.draw(canvas); - - canvas.restore(); - } - + @Override protected void dispatchDraw(Canvas canvas) { // Draw the dividers final int dividerHeight = mDividerHeight; - final Drawable overscrollHeader = mOverscrollHeader; - final Drawable overscrollFooter = mOverscrollFooter; - final boolean drawOverscrollHeader = overscrollHeader != null; - final boolean drawOverscrollFooter = overscrollFooter != null; final boolean drawDividers = dividerHeight > 0 && mDivider != null; - if (drawDividers || drawOverscrollHeader || drawOverscrollFooter) { + if (drawDividers) { // Only modify the top and bottom in the loop, we set the left and right here final Rect bounds = mTempRect; bounds.left = mPaddingLeft; @@ -3031,28 +2980,14 @@ public class ListView extends AbsListView { if (!mStackFromBottom) { int bottom = 0; - // Draw top divider or header for overscroll final int scrollY = mScrollY; - if (count > 0 && scrollY < 0) { - if (drawOverscrollHeader) { - bounds.bottom = 0; - bounds.top = scrollY; - drawOverscrollHeader(canvas, overscrollHeader, bounds); - } else if (drawDividers) { - bounds.bottom = 0; - bounds.top = -dividerHeight; - drawDivider(canvas, bounds, -1); - } - } - for (int i = 0; i < count; i++) { if ((headerDividers || first + i >= headerCount) && (footerDividers || first + i < footerLimit)) { View child = getChildAt(i); bottom = child.getBottom(); // Don't draw dividers next to items that are not enabled - if (drawDividers && - (bottom < listBottom && !(drawOverscrollFooter && i == count - 1))) { + if (drawDividers) { if ((areAllItemsSelectable || (adapter.isEnabled(first + i) && (i == count - 1 || adapter.isEnabled(first + i + 1))))) { @@ -3067,28 +3002,13 @@ public class ListView extends AbsListView { } } } - - final int overFooterBottom = mBottom + mScrollY; - if (drawOverscrollFooter && first + count == itemCount && - overFooterBottom > bottom) { - bounds.top = bottom; - bounds.bottom = overFooterBottom; - drawOverscrollFooter(canvas, overscrollFooter, bounds); - } } else { int top; int listTop = mListPadding.top; final int scrollY = mScrollY; - - if (count > 0 && drawOverscrollHeader) { - bounds.top = scrollY; - bounds.bottom = getChildAt(0).getTop(); - drawOverscrollHeader(canvas, overscrollHeader, bounds); - } - final int start = drawOverscrollHeader ? 1 : 0; - for (int i = start; i < count; i++) { + for (int i = 0; i < count; i++) { if ((headerDividers || first + i >= headerCount) && (footerDividers || first + i < footerLimit)) { View child = getChildAt(i); @@ -3114,17 +3034,10 @@ public class ListView extends AbsListView { } } - if (count > 0 && scrollY > 0) { - if (drawOverscrollFooter) { - final int absListBottom = mBottom; - bounds.top = absListBottom; - bounds.bottom = absListBottom + scrollY; - drawOverscrollFooter(canvas, overscrollFooter, bounds); - } else if (drawDividers) { - bounds.top = listBottom; - bounds.bottom = listBottom + dividerHeight; - drawDivider(canvas, bounds, -1); - } + if (count > 0 && scrollY > 0 && drawDividers) { + bounds.top = listBottom; + bounds.bottom = listBottom + dividerHeight; + drawDivider(canvas, bounds, -1); } } } @@ -3233,45 +3146,6 @@ public class ListView extends AbsListView { invalidate(); } - /** - * Sets the drawable that will be drawn above all other list content. - * This area can become visible when the user overscrolls the list. - * - * @param header The drawable to use - */ - public void setOverscrollHeader(Drawable header) { - mOverscrollHeader = header; - if (mScrollY < 0) { - invalidate(); - } - } - - /** - * @return The drawable that will be drawn above all other list content - */ - public Drawable getOverscrollHeader() { - return mOverscrollHeader; - } - - /** - * Sets the drawable that will be drawn below all other list content. - * This area can become visible when the user overscrolls the list, - * or when the list's content does not fully fill the container area. - * - * @param footer The drawable to use - */ - public void setOverscrollFooter(Drawable footer) { - mOverscrollFooter = footer; - invalidate(); - } - - /** - * @return The drawable that will be drawn below all other list content - */ - public Drawable getOverscrollFooter() { - return mOverscrollFooter; - } - @Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); @@ -3691,20 +3565,6 @@ public class ListView extends AbsListView { mCheckedIdStates.clear(); } } - - @Override - int getOverscrollMax() { - if (mStackFromBottom) { - final int childCount = getChildCount(); - if (childCount > 0) { - return Math.min(mOverscrollMax, - (getHeight() - getChildAt(0).getTop()) / OVERSCROLL_LIMIT_DIVISOR); - } else { - return mOverscrollMax; - } - } - return super.getOverscrollMax(); - } static class SavedState extends BaseSavedState { SparseBooleanArray checkState; diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java deleted file mode 100644 index e8d92de794c68..0000000000000 --- a/core/java/android/widget/OverScroller.java +++ /dev/null @@ -1,682 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.widget; - -import android.content.Context; -import android.util.FloatMath; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; -import android.widget.Scroller.MagneticScroller; - -/** - * This class encapsulates scrolling with the ability to overshoot the bounds - * of a scrolling operation. This class is a drop-in replacement for - * {@link android.widget.Scroller} in most cases. - */ -public class OverScroller { - private int mMode; - - private MagneticOverScroller mScrollerX; - private MagneticOverScroller mScrollerY; - - private final Interpolator mInterpolator; - - private static final int DEFAULT_DURATION = 250; - private static final int SCROLL_MODE = 0; - private static final int FLING_MODE = 1; - - /** - * Creates an OverScroller with a viscous fluid scroll interpolator. - * @param context - */ - public OverScroller(Context context) { - this(context, null); - } - - /** - * Creates an OverScroller with default edge bounce coefficients. - * @param context The context of this application. - * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will - * be used. - */ - public OverScroller(Context context, Interpolator interpolator) { - this(context, interpolator, MagneticOverScroller.DEFAULT_BOUNCE_COEFFICIENT, - MagneticOverScroller.DEFAULT_BOUNCE_COEFFICIENT); - } - - /** - * Creates an OverScroller. - * @param context The context of this application. - * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will - * be used. - * @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the - * velocity which is preserved in the bounce when the horizontal edge is reached. A null value - * means no bounce. - * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction. - */ - public OverScroller(Context context, Interpolator interpolator, - float bounceCoefficientX, float bounceCoefficientY) { - mInterpolator = interpolator; - mScrollerX = new MagneticOverScroller(); - mScrollerY = new MagneticOverScroller(); - MagneticScroller.initializeFromContext(context); - - mScrollerX.setBounceCoefficient(bounceCoefficientX); - mScrollerY.setBounceCoefficient(bounceCoefficientY); - } - - /** - * - * Returns whether the scroller has finished scrolling. - * - * @return True if the scroller has finished scrolling, false otherwise. - */ - public final boolean isFinished() { - return mScrollerX.mFinished && mScrollerY.mFinished; - } - - /** - * Force the finished field to a particular value. Contrary to - * {@link #abortAnimation()}, forcing the animation to finished - * does NOT cause the scroller to move to the final x and y - * position. - * - * @param finished The new finished value. - */ - public final void forceFinished(boolean finished) { - mScrollerX.mFinished = mScrollerY.mFinished = finished; - } - - /** - * Returns the current X offset in the scroll. - * - * @return The new X offset as an absolute distance from the origin. - */ - public final int getCurrX() { - return mScrollerX.mCurrentPosition; - } - - /** - * Returns the current Y offset in the scroll. - * - * @return The new Y offset as an absolute distance from the origin. - */ - public final int getCurrY() { - return mScrollerY.mCurrentPosition; - } - - /** - * @hide - * Returns the current velocity. - * - * @return The original velocity less the deceleration, norm of the X and Y velocity vector. - */ - public float getCurrVelocity() { - float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity; - squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity; - return FloatMath.sqrt(squaredNorm); - } - - /** - * Returns the start X offset in the scroll. - * - * @return The start X offset as an absolute distance from the origin. - */ - public final int getStartX() { - return mScrollerX.mStart; - } - - /** - * Returns the start Y offset in the scroll. - * - * @return The start Y offset as an absolute distance from the origin. - */ - public final int getStartY() { - return mScrollerY.mStart; - } - - /** - * Returns where the scroll will end. Valid only for "fling" scrolls. - * - * @return The final X offset as an absolute distance from the origin. - */ - public final int getFinalX() { - return mScrollerX.mFinal; - } - - /** - * Returns where the scroll will end. Valid only for "fling" scrolls. - * - * @return The final Y offset as an absolute distance from the origin. - */ - public final int getFinalY() { - return mScrollerY.mFinal; - } - - /** - * Returns how long the scroll event will take, in milliseconds. - * - * @return The duration of the scroll in milliseconds. - * - * @hide Pending removal once nothing depends on it - * @deprecated OverScrollers don't necessarily have a fixed duration. - * This function will lie to the best of its ability. - */ - public final int getDuration() { - return Math.max(mScrollerX.mDuration, mScrollerY.mDuration); - } - - /** - * Extend the scroll animation. This allows a running animation to scroll - * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}. - * - * @param extend Additional time to scroll in milliseconds. - * @see #setFinalX(int) - * @see #setFinalY(int) - * - * @hide Pending removal once nothing depends on it - * @deprecated OverScrollers don't necessarily have a fixed duration. - * Instead of setting a new final position and extending - * the duration of an existing scroll, use startScroll - * to begin a new animation. - */ - public void extendDuration(int extend) { - mScrollerX.extendDuration(extend); - mScrollerY.extendDuration(extend); - } - - /** - * Sets the final position (X) for this scroller. - * - * @param newX The new X offset as an absolute distance from the origin. - * @see #extendDuration(int) - * @see #setFinalY(int) - * - * @hide Pending removal once nothing depends on it - * @deprecated OverScroller's final position may change during an animation. - * Instead of setting a new final position and extending - * the duration of an existing scroll, use startScroll - * to begin a new animation. - */ - public void setFinalX(int newX) { - mScrollerX.setFinalPosition(newX); - } - - /** - * Sets the final position (Y) for this scroller. - * - * @param newY The new Y offset as an absolute distance from the origin. - * @see #extendDuration(int) - * @see #setFinalX(int) - * - * @hide Pending removal once nothing depends on it - * @deprecated OverScroller's final position may change during an animation. - * Instead of setting a new final position and extending - * the duration of an existing scroll, use startScroll - * to begin a new animation. - */ - public void setFinalY(int newY) { - mScrollerY.setFinalPosition(newY); - } - - /** - * Call this when you want to know the new location. If it returns true, the - * animation is not yet finished. - */ - public boolean computeScrollOffset() { - if (isFinished()) { - return false; - } - - switch (mMode) { - case SCROLL_MODE: - long time = AnimationUtils.currentAnimationTimeMillis(); - // Any scroller can be used for time, since they were started - // together in scroll mode. We use X here. - final long elapsedTime = time - mScrollerX.mStartTime; - - final int duration = mScrollerX.mDuration; - if (elapsedTime < duration) { - float q = (float) (elapsedTime) / duration; - - if (mInterpolator == null) - q = Scroller.viscousFluid(q); - else - q = mInterpolator.getInterpolation(q); - - mScrollerX.updateScroll(q); - mScrollerY.updateScroll(q); - } else { - abortAnimation(); - } - break; - - case FLING_MODE: - if (!mScrollerX.mFinished) { - if (!mScrollerX.update()) { - if (!mScrollerX.continueWhenFinished()) { - mScrollerX.finish(); - } - } - } - - if (!mScrollerY.mFinished) { - if (!mScrollerY.update()) { - if (!mScrollerY.continueWhenFinished()) { - mScrollerY.finish(); - } - } - } - - break; - } - - return true; - } - - /** - * Start scrolling by providing a starting point and the distance to travel. - * The scroll will use the default value of 250 milliseconds for the - * duration. - * - * @param startX Starting horizontal scroll offset in pixels. Positive - * numbers will scroll the content to the left. - * @param startY Starting vertical scroll offset in pixels. Positive numbers - * will scroll the content up. - * @param dx Horizontal distance to travel. Positive numbers will scroll the - * content to the left. - * @param dy Vertical distance to travel. Positive numbers will scroll the - * content up. - */ - public void startScroll(int startX, int startY, int dx, int dy) { - startScroll(startX, startY, dx, dy, DEFAULT_DURATION); - } - - /** - * Start scrolling by providing a starting point and the distance to travel. - * - * @param startX Starting horizontal scroll offset in pixels. Positive - * numbers will scroll the content to the left. - * @param startY Starting vertical scroll offset in pixels. Positive numbers - * will scroll the content up. - * @param dx Horizontal distance to travel. Positive numbers will scroll the - * content to the left. - * @param dy Vertical distance to travel. Positive numbers will scroll the - * content up. - * @param duration Duration of the scroll in milliseconds. - */ - public void startScroll(int startX, int startY, int dx, int dy, int duration) { - mMode = SCROLL_MODE; - mScrollerX.startScroll(startX, dx, duration); - mScrollerY.startScroll(startY, dy, duration); - } - - /** - * Call this when you want to 'spring back' into a valid coordinate range. - * - * @param startX Starting X coordinate - * @param startY Starting Y coordinate - * @param minX Minimum valid X value - * @param maxX Maximum valid X value - * @param minY Minimum valid Y value - * @param maxY Minimum valid Y value - * @return true if a springback was initiated, false if startX and startY were - * already within the valid range. - */ - public boolean springback(int startX, int startY, int minX, int maxX, int minY, int maxY) { - mMode = FLING_MODE; - - // Make sure both methods are called. - final boolean spingbackX = mScrollerX.springback(startX, minX, maxX); - final boolean spingbackY = mScrollerY.springback(startY, minY, maxY); - return spingbackX || spingbackY; - } - - public void fling(int startX, int startY, int velocityX, int velocityY, - int minX, int maxX, int minY, int maxY) { - fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0); - } - - /** - * Start scrolling based on a fling gesture. The distance traveled will - * depend on the initial velocity of the fling. - * - * @param startX Starting point of the scroll (X) - * @param startY Starting point of the scroll (Y) - * @param velocityX Initial velocity of the fling (X) measured in pixels per - * second. - * @param velocityY Initial velocity of the fling (Y) measured in pixels per - * second - * @param minX Minimum X value. The scroller will not scroll past this point - * unless overX > 0. If overfling is allowed, it will use minX as - * a springback boundary. - * @param maxX Maximum X value. The scroller will not scroll past this point - * unless overX > 0. If overfling is allowed, it will use maxX as - * a springback boundary. - * @param minY Minimum Y value. The scroller will not scroll past this point - * unless overY > 0. If overfling is allowed, it will use minY as - * a springback boundary. - * @param maxY Maximum Y value. The scroller will not scroll past this point - * unless overY > 0. If overfling is allowed, it will use maxY as - * a springback boundary. - * @param overX Overfling range. If > 0, horizontal overfling in either - * direction will be possible. - * @param overY Overfling range. If > 0, vertical overfling in either - * direction will be possible. - */ - public void fling(int startX, int startY, int velocityX, int velocityY, - int minX, int maxX, int minY, int maxY, int overX, int overY) { - mMode = FLING_MODE; - mScrollerX.fling(startX, velocityX, minX, maxX, overX); - mScrollerY.fling(startY, velocityY, minY, maxY, overY); - } - - /** - * Notify the scroller that we've reached a horizontal boundary. - * Normally the information to handle this will already be known - * when the animation is started, such as in a call to one of the - * fling functions. However there are cases where this cannot be known - * in advance. This function will transition the current motion and - * animate from startX to finalX as appropriate. - * - * @param startX Starting/current X position - * @param finalX Desired final X position - * @param overX Magnitude of overscroll allowed. This should be the maximum - * desired distance from finalX. Absolute value - must be positive. - */ - public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) { - mScrollerX.notifyEdgeReached(startX, finalX, overX); - } - - /** - * Notify the scroller that we've reached a vertical boundary. - * Normally the information to handle this will already be known - * when the animation is started, such as in a call to one of the - * fling functions. However there are cases where this cannot be known - * in advance. This function will animate a parabolic motion from - * startY to finalY. - * - * @param startY Starting/current Y position - * @param finalY Desired final Y position - * @param overY Magnitude of overscroll allowed. This should be the maximum - * desired distance from finalY. - */ - public void notifyVerticalEdgeReached(int startY, int finalY, int overY) { - mScrollerY.notifyEdgeReached(startY, finalY, overY); - } - - /** - * Returns whether the current Scroller is currently returning to a valid position. - * Valid bounds were provided by the - * {@link #fling(int, int, int, int, int, int, int, int, int, int)} method. - * - * One should check this value before calling - * {@link #startScroll(int, int, int, int)} as the interpolation currently in progress - * to restore a valid position will then be stopped. The caller has to take into account - * the fact that the started scroll will start from an overscrolled position. - * - * @return true when the current position is overscrolled and in the process of - * interpolating back to a valid value. - */ - public boolean isOverscrolled() { - return ((!mScrollerX.mFinished && - mScrollerX.mState != MagneticOverScroller.TO_EDGE) || - (!mScrollerY.mFinished && - mScrollerY.mState != MagneticOverScroller.TO_EDGE)); - } - - /** - * Stops the animation. Contrary to {@link #forceFinished(boolean)}, - * aborting the animating causes the scroller to move to the final x and y - * positions. - * - * @see #forceFinished(boolean) - */ - public void abortAnimation() { - mScrollerX.finish(); - mScrollerY.finish(); - } - - /** - * Returns the time elapsed since the beginning of the scrolling. - * - * @return The elapsed time in milliseconds. - * - * @hide - */ - public int timePassed() { - final long time = AnimationUtils.currentAnimationTimeMillis(); - final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime); - return (int) (time - startTime); - } - - static class MagneticOverScroller extends Scroller.MagneticScroller { - private static final int TO_EDGE = 0; - private static final int TO_BOUNDARY = 1; - private static final int TO_BOUNCE = 2; - - private int mState = TO_EDGE; - - // The allowed overshot distance before boundary is reached. - private int mOver; - - // Duration in milliseconds to go back from edge to edge. Springback is half of it. - private static final int OVERSCROLL_SPRINGBACK_DURATION = 200; - - // Oscillation period - private static final float TIME_COEF = - 1000.0f * (float) Math.PI / OVERSCROLL_SPRINGBACK_DURATION; - - // If the velocity is smaller than this value, no bounce is triggered - // when the edge limits are reached (would result in a zero pixels - // displacement anyway). - private static final float MINIMUM_VELOCITY_FOR_BOUNCE = 140.0f; - - // Proportion of the velocity that is preserved when the edge is reached. - private static final float DEFAULT_BOUNCE_COEFFICIENT = 0.16f; - - private float mBounceCoefficient = DEFAULT_BOUNCE_COEFFICIENT; - - void setBounceCoefficient(float coefficient) { - mBounceCoefficient = coefficient; - } - - boolean springback(int start, int min, int max) { - mFinished = true; - - mStart = start; - mVelocity = 0; - - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mDuration = 0; - - if (start < min) { - startSpringback(start, min, false); - } else if (start > max) { - startSpringback(start, max, true); - } - - return !mFinished; - } - - private void startSpringback(int start, int end, boolean positive) { - mFinished = false; - mState = TO_BOUNCE; - mStart = mFinal = end; - mDuration = OVERSCROLL_SPRINGBACK_DURATION; - mStartTime -= OVERSCROLL_SPRINGBACK_DURATION / 2; - mVelocity = (int) (Math.abs(end - start) * TIME_COEF * (positive ? 1.0 : -1.0f)); - } - - void fling(int start, int velocity, int min, int max, int over) { - mState = TO_EDGE; - mOver = over; - - super.fling(start, velocity, min, max); - - if (start > max) { - if (start >= max + over) { - springback(max + over, min, max); - } else { - if (velocity <= 0) { - springback(start, min, max); - } else { - long time = AnimationUtils.currentAnimationTimeMillis(); - final double durationSinceEdge = - Math.atan((start-max) * TIME_COEF / velocity) / TIME_COEF; - mStartTime = (int) (time - 1000.0f * durationSinceEdge); - - // Simulate a bounce that started from edge - mStart = max; - - mVelocity = (int) (velocity / Math.cos(durationSinceEdge * TIME_COEF)); - - onEdgeReached(); - } - } - } else { - if (start < min) { - if (start <= min - over) { - springback(min - over, min, max); - } else { - if (velocity >= 0) { - springback(start, min, max); - } else { - long time = AnimationUtils.currentAnimationTimeMillis(); - final double durationSinceEdge = - Math.atan((start-min) * TIME_COEF / velocity) / TIME_COEF; - mStartTime = (int) (time - 1000.0f * durationSinceEdge); - - // Simulate a bounce that started from edge - mStart = min; - - mVelocity = (int) (velocity / Math.cos(durationSinceEdge * TIME_COEF)); - - onEdgeReached(); - } - - } - } - } - } - - void notifyEdgeReached(int start, int end, int over) { - mDeceleration = getDeceleration(mVelocity); - - // Local time, used to compute edge crossing time. - float timeCurrent = mCurrVelocity / mDeceleration; - final int distance = end - start; - float timeEdge = -(float) Math.sqrt((2.0f * distance / mDeceleration) - + (timeCurrent * timeCurrent)); - - mVelocity = (int) (mDeceleration * timeEdge); - - // Simulate a symmetric bounce that started from edge - mStart = end; - - mOver = over; - - long time = AnimationUtils.currentAnimationTimeMillis(); - mStartTime = (int) (time - 1000.0f * (timeCurrent - timeEdge)); - - onEdgeReached(); - } - - private void onEdgeReached() { - // mStart, mVelocity and mStartTime were adjusted to their values when edge was reached. - final float distance = mVelocity / TIME_COEF; - - if (Math.abs(distance) < mOver) { - // Spring force will bring us back to final position - mState = TO_BOUNCE; - mFinal = mStart; - mDuration = OVERSCROLL_SPRINGBACK_DURATION; - } else { - // Velocity is too high, we will hit the boundary limit - mState = TO_BOUNDARY; - int over = mVelocity > 0 ? mOver : -mOver; - mFinal = mStart + over; - mDuration = (int) (1000.0f * Math.asin(over / distance) / TIME_COEF); - } - } - - @Override - boolean continueWhenFinished() { - switch (mState) { - case TO_EDGE: - // Duration from start to null velocity - int duration = (int) (-1000.0f * mVelocity / mDeceleration); - if (mDuration < duration) { - // If the animation was clamped, we reached the edge - mStart = mFinal; - // Speed when edge was reached - mVelocity = (int) (mVelocity + mDeceleration * mDuration / 1000.0f); - mStartTime += mDuration; - onEdgeReached(); - } else { - // Normal stop, no need to continue - return false; - } - break; - case TO_BOUNDARY: - mStartTime += mDuration; - startSpringback(mFinal, mFinal - (mVelocity > 0 ? mOver:-mOver), mVelocity > 0); - break; - case TO_BOUNCE: - //mVelocity = (int) (mVelocity * BOUNCE_COEFFICIENT); - mVelocity = (int) (mVelocity * mBounceCoefficient); - if (Math.abs(mVelocity) < MINIMUM_VELOCITY_FOR_BOUNCE) { - return false; - } - mStartTime += mDuration; - break; - } - - update(); - return true; - } - - /* - * Update the current position and velocity for current time. Returns - * true if update has been done and false if animation duration has been - * reached. - */ - @Override - boolean update() { - final long time = AnimationUtils.currentAnimationTimeMillis(); - final long duration = time - mStartTime; - - if (duration > mDuration) { - return false; - } - - double distance; - final float t = duration / 1000.0f; - if (mState == TO_EDGE) { - mCurrVelocity = mVelocity + mDeceleration * t; - distance = mVelocity * t + mDeceleration * t * t / 2.0f; - } else { - final float d = t * TIME_COEF; - mCurrVelocity = mVelocity * (float)Math.cos(d); - distance = mVelocity / TIME_COEF * Math.sin(d); - } - - mCurrentPosition = mStart + (int) distance; - return true; - } - } -} diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 23e1234f7044d..a02aa00eb718e 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -59,7 +59,7 @@ public class ScrollView extends FrameLayout { private long mLastScroll; private final Rect mTempRect = new Rect(); - private OverScroller mScroller; + private Scroller mScroller; /** * Flag to indicate that we are moving focus ourselves. This is so the @@ -185,7 +185,7 @@ public class ScrollView extends FrameLayout { private void initScrollView() { - mScroller = new OverScroller(getContext()); + mScroller = new Scroller(getContext()); setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); @@ -453,9 +453,6 @@ 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); @@ -513,11 +510,7 @@ public class ScrollView extends FrameLayout { final int deltaY = (int) (mLastMotionY - y); mLastMotionY = y; - final int oldX = mScrollX; - final int oldY = mScrollY; - overscrollBy(0, deltaY, 0, mScrollY, 0, getScrollRange(), - 0, getOverscrollMax(), true); - onScrollChanged(mScrollX, mScrollY, oldX, oldY); + scrollBy(0, deltaY); } break; case MotionEvent.ACTION_UP: @@ -526,15 +519,8 @@ public class ScrollView extends FrameLayout { velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId); - 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(); - } - } + if (getChildCount() > 0 && Math.abs(initialVelocity) > mMinimumVelocity) { + fling(-initialVelocity); } mActivePointerId = INVALID_POINTER; @@ -548,9 +534,6 @@ public class ScrollView extends FrameLayout { 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) { @@ -583,32 +566,6 @@ 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 getOverscrollMax() { - int childCount = getChildCount(); - int containerOverscroll = (getHeight() - mPaddingBottom - mPaddingTop) / 3; - if (childCount > 0) { - return Math.min(containerOverscroll, getChildAt(0).getHeight() / 3); - } else { - return containerOverscroll; - } - } - private int getScrollRange() { int scrollRange = 0; if (getChildCount() > 0) { @@ -995,16 +952,7 @@ public class ScrollView extends FrameLayout { return contentHeight; } - 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; + return getChildAt(0).getBottom(); } @Override @@ -1065,12 +1013,17 @@ public class ScrollView extends FrameLayout { int x = mScroller.getCurrX(); int y = mScroller.getCurrY(); - if (oldX != x || oldY != y) { - overscrollBy(x - oldX, y - oldY, oldX, oldY, 0, getScrollRange(), - 0, getOverscrollMax(), false); - onScrollChanged(mScrollX, mScrollY, oldX, oldY); + 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); + } } - + // Keep on drawing until the animation has finished. postInvalidate(); } @@ -1304,7 +1257,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), 0, height/2); + Math.max(0, bottom - height)); final boolean movingDown = velocityY > 0; diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java index abb3e321d6751..11dab02f68e88 100644 --- a/core/java/android/widget/Scroller.java +++ b/core/java/android/widget/Scroller.java @@ -16,10 +16,8 @@ package android.widget; - import android.content.Context; import android.hardware.SensorManager; -import android.util.FloatMath; import android.view.ViewConfiguration; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -27,34 +25,48 @@ import android.view.animation.Interpolator; /** * This class encapsulates scrolling. The duration of the scroll - * is either specified along with the distance or depends on the initial fling velocity. - * Past this time, the scrolling is automatically moved to its final stage and - * computeScrollOffset() will always return false to indicate that scrolling is over. + * can be passed in the constructor and specifies the maximum time that + * the scrolling animation should take. Past this time, the scrolling is + * automatically moved to its final stage and computeScrollOffset() + * will always return false to indicate that scrolling is over. */ public class Scroller { private int mMode; - private MagneticScroller mScrollerX; - private MagneticScroller mScrollerY; + private int mStartX; + private int mStartY; + private int mFinalX; + private int mFinalY; - private final Interpolator mInterpolator; + private int mMinX; + private int mMaxX; + private int mMinY; + private int mMaxY; + + private int mCurrX; + private int mCurrY; + private long mStartTime; + private int mDuration; + private float mDurationReciprocal; + private float mDeltaX; + private float mDeltaY; + private float mViscousFluidScale; + private float mViscousFluidNormalize; + private boolean mFinished; + private Interpolator mInterpolator; + + private float mCoeffX = 0.0f; + private float mCoeffY = 1.0f; + private float mVelocity; private static final int DEFAULT_DURATION = 250; private static final int SCROLL_MODE = 0; private static final int FLING_MODE = 1; - // This controls the viscous fluid effect (how much of it) - private final static float VISCOUS_FLUID_SCALE = 8.0f; - private static float VISCOUS_FLUID_NORMALIZE; - - static { - // Set a neutral value that will be used in the next call to viscousFluid(). - VISCOUS_FLUID_NORMALIZE = 1.0f; - VISCOUS_FLUID_NORMALIZE = 1.0f / viscousFluid(1.0f); - } + private final float mDeceleration; /** - * Create a Scroller with a viscous fluid scroll interpolator. + * Create a Scroller with the default duration and interpolator. */ public Scroller(Context context) { this(context, null); @@ -65,13 +77,15 @@ public class Scroller { * null, the default (viscous) interpolator will be used. */ public Scroller(Context context, Interpolator interpolator) { - mScrollerX = new MagneticScroller(); - mScrollerY = new MagneticScroller(); - MagneticScroller.initializeFromContext(context); - + mFinished = true; mInterpolator = interpolator; + float ppi = context.getResources().getDisplayMetrics().density * 160.0f; + mDeceleration = SensorManager.GRAVITY_EARTH // g (m/s^2) + * 39.37f // inch/meter + * ppi // pixels per inch + * ViewConfiguration.getScrollFriction(); } - + /** * * Returns whether the scroller has finished scrolling. @@ -79,148 +93,150 @@ public class Scroller { * @return True if the scroller has finished scrolling, false otherwise. */ public final boolean isFinished() { - return mScrollerX.mFinished && mScrollerY.mFinished; + return mFinished; } - + /** * Force the finished field to a particular value. - * + * * @param finished The new finished value. */ public final void forceFinished(boolean finished) { - mScrollerX.mFinished = mScrollerY.mFinished = finished; + mFinished = finished; } - + /** * Returns how long the scroll event will take, in milliseconds. * * @return The duration of the scroll in milliseconds. */ public final int getDuration() { - return Math.max(mScrollerX.mDuration, mScrollerY.mDuration); + return mDuration; } - + /** - * Returns the current X offset in the scroll. + * Returns the current X offset in the scroll. * * @return The new X offset as an absolute distance from the origin. */ public final int getCurrX() { - return mScrollerX.mCurrentPosition; + return mCurrX; } - + /** - * Returns the current Y offset in the scroll. + * Returns the current Y offset in the scroll. * * @return The new Y offset as an absolute distance from the origin. */ public final int getCurrY() { - return mScrollerY.mCurrentPosition; + return mCurrY; } - + /** * @hide * Returns the current velocity. - * - * @return The original velocity less the deceleration, norm of the X and Y velocity vector. + * + * @return The original velocity less the deceleration. Result may be + * negative. */ public float getCurrVelocity() { - float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity; - squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity; - return FloatMath.sqrt(squaredNorm); + return mVelocity - mDeceleration * timePassed() / 2000.0f; } /** - * Returns the start X offset in the scroll. + * Returns the start X offset in the scroll. * * @return The start X offset as an absolute distance from the origin. */ public final int getStartX() { - return mScrollerX.mStart; + return mStartX; } - + /** - * Returns the start Y offset in the scroll. + * Returns the start Y offset in the scroll. * * @return The start Y offset as an absolute distance from the origin. */ public final int getStartY() { - return mScrollerY.mStart; + return mStartY; } - + /** * Returns where the scroll will end. Valid only for "fling" scrolls. * * @return The final X offset as an absolute distance from the origin. */ public final int getFinalX() { - return mScrollerX.mFinal; + return mFinalX; } - + /** * Returns where the scroll will end. Valid only for "fling" scrolls. * * @return The final Y offset as an absolute distance from the origin. */ public final int getFinalY() { - return mScrollerY.mFinal; + return mFinalY; } /** - * Call this when you want to know the new location. If it returns true, the - * animation is not yet finished. - */ + * Call this when you want to know the new location. If it returns true, + * the animation is not yet finished. loc will be altered to provide the + * new location. + */ public boolean computeScrollOffset() { - if (isFinished()) { + if (mFinished) { return false; } - switch (mMode) { + int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); + + if (timePassed < mDuration) { + switch (mMode) { case SCROLL_MODE: - long time = AnimationUtils.currentAnimationTimeMillis(); - // Any scroller can be used for time, since they were started - // together in scroll mode. We use X here. - final long elapsedTime = time - mScrollerX.mStartTime; - - final int duration = mScrollerX.mDuration; - if (elapsedTime < duration) { - float q = (float) (elapsedTime) / duration; - - if (mInterpolator == null) - q = viscousFluid(q); - else - q = mInterpolator.getInterpolation(q); - - mScrollerX.updateScroll(q); - mScrollerY.updateScroll(q); - } else { - abortAnimation(); + float x = (float)timePassed * mDurationReciprocal; + + if (mInterpolator == null) + x = viscousFluid(x); + else + x = mInterpolator.getInterpolation(x); + + mCurrX = mStartX + Math.round(x * mDeltaX); + mCurrY = mStartY + Math.round(x * mDeltaY); + if ((mCurrX == mFinalX) && (mCurrY == mFinalY)) { + mFinished = true; } break; - case FLING_MODE: - if (!mScrollerX.mFinished) { - if (!mScrollerX.update()) { - if (!mScrollerX.continueWhenFinished()) { - mScrollerX.finish(); - } - } - } + float timePassedSeconds = timePassed / 1000.0f; + float distance = (mVelocity * timePassedSeconds) + - (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f); + + mCurrX = mStartX + Math.round(distance * mCoeffX); + // Pin to mMinX <= mCurrX <= mMaxX + mCurrX = Math.min(mCurrX, mMaxX); + mCurrX = Math.max(mCurrX, mMinX); + + mCurrY = mStartY + Math.round(distance * mCoeffY); + // Pin to mMinY <= mCurrY <= mMaxY + mCurrY = Math.min(mCurrY, mMaxY); + mCurrY = Math.max(mCurrY, mMinY); - if (!mScrollerY.mFinished) { - if (!mScrollerY.update()) { - if (!mScrollerY.continueWhenFinished()) { - mScrollerY.finish(); - } - } + if (mCurrX == mFinalX && mCurrY == mFinalY) { + mFinished = true; } - + break; + } + } + else { + mCurrX = mFinalX; + mCurrY = mFinalY; + mFinished = true; } - return true; } - + /** * Start scrolling by providing a starting point and the distance to travel. * The scroll will use the default value of 250 milliseconds for the @@ -254,39 +270,83 @@ public class Scroller { */ public void startScroll(int startX, int startY, int dx, int dy, int duration) { mMode = SCROLL_MODE; - mScrollerX.startScroll(startX, dx, duration); - mScrollerY.startScroll(startY, dy, duration); + mFinished = false; + mDuration = duration; + mStartTime = AnimationUtils.currentAnimationTimeMillis(); + mStartX = startX; + mStartY = startY; + mFinalX = startX + dx; + mFinalY = startY + dy; + mDeltaX = dx; + mDeltaY = dy; + mDurationReciprocal = 1.0f / (float) mDuration; + // This controls the viscous fluid effect (how much of it) + mViscousFluidScale = 8.0f; + // must be set to 1.0 (used in viscousFluid()) + mViscousFluidNormalize = 1.0f; + mViscousFluidNormalize = 1.0f / viscousFluid(1.0f); } /** - * Start scrolling based on a fling gesture. The distance traveled will - * depend on the initial velocity of the fling. Velocity is slowed down by a - * constant deceleration until it reaches 0 or the limits are reached. + * Start scrolling based on a fling gesture. The distance travelled will + * depend on the initial velocity of the fling. * * @param startX Starting point of the scroll (X) * @param startY Starting point of the scroll (Y) * @param velocityX Initial velocity of the fling (X) measured in pixels per - * second. + * second. * @param velocityY Initial velocity of the fling (Y) measured in pixels per - * second. + * second * @param minX Minimum X value. The scroller will not scroll past this - * point. + * point. * @param maxX Maximum X value. The scroller will not scroll past this - * point. + * point. * @param minY Minimum Y value. The scroller will not scroll past this - * point. + * point. * @param maxY Maximum Y value. The scroller will not scroll past this - * point. + * point. */ public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) { mMode = FLING_MODE; - mScrollerX.fling(startX, velocityX, minX, maxX); - mScrollerY.fling(startY, velocityY, minY, maxY); - } + mFinished = false; - static float viscousFluid(float x) { - x *= VISCOUS_FLUID_SCALE; + float velocity = (float)Math.hypot(velocityX, velocityY); + + mVelocity = velocity; + mDuration = (int) (1000 * velocity / mDeceleration); // Duration is in + // milliseconds + mStartTime = AnimationUtils.currentAnimationTimeMillis(); + mStartX = startX; + mStartY = startY; + + mCoeffX = velocity == 0 ? 1.0f : velocityX / velocity; + mCoeffY = velocity == 0 ? 1.0f : velocityY / velocity; + + int totalDistance = (int) ((velocity * velocity) / (2 * mDeceleration)); + + mMinX = minX; + mMaxX = maxX; + mMinY = minY; + mMaxY = maxY; + + + mFinalX = startX + Math.round(totalDistance * mCoeffX); + // Pin to mMinX <= mFinalX <= mMaxX + mFinalX = Math.min(mFinalX, mMaxX); + mFinalX = Math.max(mFinalX, mMinX); + + mFinalY = startY + Math.round(totalDistance * mCoeffY); + // Pin to mMinY <= mFinalY <= mMaxY + mFinalY = Math.min(mFinalY, mMaxY); + mFinalY = Math.max(mFinalY, mMinY); + } + + + + private float viscousFluid(float x) + { + x *= mViscousFluidScale; if (x < 1.0f) { x -= (1.0f - (float)Math.exp(-x)); } else { @@ -294,237 +354,70 @@ public class Scroller { x = 1.0f - (float)Math.exp(1.0f - x); x = start + x * (1.0f - start); } - x *= VISCOUS_FLUID_NORMALIZE; + x *= mViscousFluidNormalize; return x; } - + /** * Stops the animation. Contrary to {@link #forceFinished(boolean)}, * aborting the animating cause the scroller to move to the final x and y * position - * + * * @see #forceFinished(boolean) */ public void abortAnimation() { - mScrollerX.finish(); - mScrollerY.finish(); + mCurrX = mFinalX; + mCurrY = mFinalY; + mFinished = true; } - + /** * Extend the scroll animation. This allows a running animation to scroll * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}. - * + * * @param extend Additional time to scroll in milliseconds. * @see #setFinalX(int) * @see #setFinalY(int) */ public void extendDuration(int extend) { - mScrollerX.extendDuration(extend); - mScrollerY.extendDuration(extend); + int passed = timePassed(); + mDuration = passed + extend; + mDurationReciprocal = 1.0f / (float)mDuration; + mFinished = false; } /** * Returns the time elapsed since the beginning of the scrolling. - * + * * @return The elapsed time in milliseconds. */ public int timePassed() { - final long time = AnimationUtils.currentAnimationTimeMillis(); - final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime); - return (int) (time - startTime); + return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); } /** * Sets the final position (X) for this scroller. - * + * * @param newX The new X offset as an absolute distance from the origin. * @see #extendDuration(int) * @see #setFinalY(int) */ public void setFinalX(int newX) { - mScrollerX.setFinalPosition(newX); + mFinalX = newX; + mDeltaX = mFinalX - mStartX; + mFinished = false; } /** * Sets the final position (Y) for this scroller. - * + * * @param newY The new Y offset as an absolute distance from the origin. * @see #extendDuration(int) * @see #setFinalX(int) */ public void setFinalY(int newY) { - mScrollerY.setFinalPosition(newY); - } - - static class MagneticScroller { - // Initial position - int mStart; - - // Current position - int mCurrentPosition; - - // Final position - int mFinal; - - // Initial velocity - int mVelocity; - - // Current velocity - float mCurrVelocity; - - // Constant current deceleration - float mDeceleration; - - // Animation starting time, in system milliseconds - long mStartTime; - - // Animation duration, in milliseconds - int mDuration; - - // Whether the animation is currently in progress - boolean mFinished; - - // Constant gravity value, used to scale deceleration - static float GRAVITY; - - static void initializeFromContext(Context context) { - final float ppi = context.getResources().getDisplayMetrics().density * 160.0f; - GRAVITY = SensorManager.GRAVITY_EARTH // g (m/s^2) - * 39.37f // inch/meter - * ppi // pixels per inch - * ViewConfiguration.getScrollFriction(); - } - - MagneticScroller() { - mFinished = true; - } - - void updateScroll(float q) { - mCurrentPosition = mStart + Math.round(q * (mFinal - mStart)); - } - - /* - * Update the current position and velocity for current time. Returns - * true if update has been done and false if animation duration has been - * reached. - */ - boolean update() { - final long time = AnimationUtils.currentAnimationTimeMillis(); - final long duration = time - mStartTime; - - if (duration > mDuration) { - return false; - } - - final float t = duration / 1000.0f; - mCurrVelocity = mVelocity + mDeceleration * t; - final float distance = mVelocity * t + mDeceleration * t * t / 2.0f; - mCurrentPosition = mStart + (int) distance; - - return true; - } - - /* - * Get a signed deceleration that will reduce the velocity. - */ - static float getDeceleration(int velocity) { - return velocity > 0 ? -GRAVITY : GRAVITY; - } - - /* - * Returns the time (in milliseconds) it will take to go from start to end. - */ - static int computeDuration(int start, int end, float initialVelocity, float deceleration) { - final int distance = start - end; - final float discriminant = initialVelocity * initialVelocity - 2.0f * deceleration - * distance; - if (discriminant >= 0.0f) { - float delta = (float) Math.sqrt(discriminant); - if (deceleration < 0.0f) { - delta = -delta; - } - return (int) (1000.0f * (-initialVelocity - delta) / deceleration); - } - - // End position can not be reached - return 0; - } - - void startScroll(int start, int distance, int duration) { - mFinished = false; - - mStart = start; - mFinal = start + distance; - - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mDuration = duration; - - // Unused - mDeceleration = 0.0f; - mVelocity = 0; - } - - void fling(int start, int velocity, int min, int max) { - mFinished = false; - - mStart = start; - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - - mVelocity = velocity; - - mDeceleration = getDeceleration(velocity); - - // A start from an invalid position immediately brings back to a valid position - if (mStart < min) { - mDuration = 0; - mFinal = min; - return; - } - - if (mStart > max) { - mDuration = 0; - mFinal = max; - return; - } - - // Duration are expressed in milliseconds - mDuration = (int) (-1000.0f * velocity / mDeceleration); - - mFinal = start - Math.round((velocity * velocity) / (2.0f * mDeceleration)); - - // Clamp to a valid final position - if (mFinal < min) { - mFinal = min; - mDuration = computeDuration(mStart, min, mVelocity, mDeceleration); - } - - if (mFinal > max) { - mFinal = max; - mDuration = computeDuration(mStart, max, mVelocity, mDeceleration); - } - } - - void finish() { - mCurrentPosition = mFinal; - // Not reset since WebView relies on this value for fast fling. - // mCurrVelocity = 0.0f; - mFinished = true; - } - - boolean continueWhenFinished() { - return false; - } - - void setFinalPosition(int position) { - mFinal = position; - mFinished = false; - } - - void extendDuration(int extend) { - final long time = AnimationUtils.currentAnimationTimeMillis(); - final int elapsedTime = (int) (time - mStartTime); - mDuration = elapsedTime + extend; - mFinished = false; - } + mFinalY = newY; + mDeltaY = mFinalY - mStartY; + mFinished = false; } } diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml index 25a41f867df93..7ae68f900b451 100644 --- a/core/res/res/layout/alert_dialog.xml +++ b/core/res/res/layout/alert_dialog.xml @@ -80,8 +80,7 @@ android:paddingTop="2dip" android:paddingBottom="12dip" android:paddingLeft="14dip" - android:paddingRight="10dip" - android:overscrollMode="ifContentScrolls"> + android:paddingRight="10dip"> + android:layout_height="match_parent"> + android:scrollbars="vertical" /> diff --git a/core/res/res/layout/status_bar_expanded.xml b/core/res/res/layout/status_bar_expanded.xml index d443247834e25..30138a742c74d 100644 --- a/core/res/res/layout/status_bar_expanded.xml +++ b/core/res/res/layout/status_bar_expanded.xml @@ -86,7 +86,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fadingEdge="none" - android:overscrollMode="ifContentScrolls" > public void sayHello(View v) method of your context (typically, your Activity). --> - - - - - - - - - - - - - - diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 940cbf72dae54..98c3a0af6ac42 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1230,13 +1230,10 @@ - - - - - - - + + + +