Loading core/java/android/widget/AbsListView.java +268 −111 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.widget; import com.android.internal.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; Loading @@ -30,6 +32,7 @@ import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; Loading @@ -41,14 +44,12 @@ import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.ContextMenu.ContextMenuInfo; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionWrapper; import android.view.inputmethod.InputMethodManager; import android.view.ContextMenu.ContextMenuInfo; import com.android.internal.R; import java.util.ArrayList; import java.util.List; Loading Loading @@ -126,6 +127,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> 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 */ Loading Loading @@ -1915,6 +1927,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLayoutMode = LAYOUT_NORMAL; layoutChildren(); } } else { int touchMode = mTouchMode; if (touchMode == TOUCH_MODE_OVERSCROLL || touchMode == TOUCH_MODE_OVERFLING) { mScrollY = 0; if (mFlingRunnable != null) { mFlingRunnable.endFling(); } } } } Loading Loading @@ -1947,6 +1967,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te switch (action) { case MotionEvent.ACTION_DOWN: { switch (mTouchMode) { case TOUCH_MODE_OVERFLING: { mFlingRunnable.endFling(); mTouchMode = TOUCH_MODE_OVERSCROLL; mLastY = y; mMotionCorrection = 0; break; } default: { int motionPosition = pointToPosition(x, y); if (!mDataChanged) { if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0) Loading Loading @@ -1986,6 +2016,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLastY = Integer.MIN_VALUE; break; } } break; } case MotionEvent.ACTION_MOVE: { deltaY = y - mMotionY; Loading @@ -2008,17 +2041,61 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (y != mLastY) { deltaY -= mMotionCorrection; int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY; int motionViewPrevTop = 0; View motionView = this.getChildAt(mMotionPosition - mFirstPosition); if (motionView != null) { motionViewPrevTop = motionView.getTop(); } // No need to do all this work if we're not going to move anyway if (incrementalDeltaY != 0) { trackMotionScroll(deltaY, incrementalDeltaY); } // Check to see if we have bumped into the scroll limit View motionView = this.getChildAt(mMotionPosition - mFirstPosition); motionView = this.getChildAt(mMotionPosition - mFirstPosition); if (motionView != null) { // Check if the top of the motion view is where it is // supposed to be if (motionView.getTop() != mMotionViewNewTop) { final int motionViewRealTop = motionView.getTop(); final int motionViewNewTop = mMotionViewNewTop; if (motionViewRealTop != motionViewNewTop) { // Apply overscroll mScrollY -= incrementalDeltaY - (motionViewRealTop - motionViewPrevTop); 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) { int topOffset = motionView.getTop() - mMotionViewNewTop; 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 = findMotionRow(y); Loading @@ -2029,6 +2106,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mMotionY = y; mMotionPosition = motionPosition; } } else { mScrollY -= incrementalDeltaY; invalidate(); } mLastY = y; } Loading Loading @@ -2120,6 +2200,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mFlingRunnable = new FlingRunnable(); } reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); mFlingRunnable.start(-initialVelocity); } else { mTouchMode = TOUCH_MODE_REST; Loading @@ -2130,6 +2211,24 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mTouchMode = TOUCH_MODE_REST; 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(); reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); if (Math.abs(initialVelocity) > mMinimumVelocity) { mFlingRunnable.startOverfling(-initialVelocity); } else { mFlingRunnable.startSpringback(); } break; } setPressed(false); Loading Loading @@ -2157,6 +2256,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> 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); Loading @@ -2175,7 +2287,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mVelocityTracker = null; } } } } return true; Loading Loading @@ -2205,8 +2317,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te switch (action) { case MotionEvent.ACTION_DOWN: { int touchMode = mTouchMode; if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) { return true; } int motionPosition = findMotionRow(y); if (mTouchMode != TOUCH_MODE_FLING && motionPosition >= 0) { if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) { // User clicked on an actual view (and was not stopping a fling). // Remember where the motion event started v = getChildAt(motionPosition - mFirstPosition); Loading @@ -2218,7 +2335,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te clearScrollingCache(); } mLastY = Integer.MIN_VALUE; if (mTouchMode == TOUCH_MODE_FLING) { if (touchMode == TOUCH_MODE_FLING) { return true; } break; Loading Loading @@ -2293,18 +2410,18 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te /** * Tracks the decay of a fling scroll */ private Scroller mScroller; private OverScroller mScroller; /** * Y value reported by mScroller on the previous fling */ private int mLastFlingY; public FlingRunnable() { mScroller = new Scroller(getContext()); FlingRunnable() { mScroller = new OverScroller(getContext()); } public void start(int initialVelocity) { void start(int initialVelocity) { int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0; mLastFlingY = initialY; mScroller.fling(0, initialY, 0, initialVelocity, Loading @@ -2320,23 +2437,40 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } void startSpringback() { if (mScroller.springback(0, mScrollY, 0, 0, 0, 0)) { mTouchMode = TOUCH_MODE_OVERFLING; invalidate(); post(this); } } void startOverfling(int initialVelocity) { mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, 0, 0, 0, getHeight()); mTouchMode = TOUCH_MODE_OVERFLING; invalidate(); post(this); } private void endFling() { mTouchMode = TOUCH_MODE_REST; reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); clearScrollingCache(); removeCallbacks(this); } public void run() { if (mTouchMode != TOUCH_MODE_FLING) { switch (mTouchMode) { default: return; } case TOUCH_MODE_FLING: { if (mItemCount == 0 || getChildCount() == 0) { endFling(); return; } final Scroller scroller = mScroller; final OverScroller scroller = mScroller; boolean more = scroller.computeScrollOffset(); final int y = scroller.getCurrY(); Loading Loading @@ -2365,6 +2499,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta); } // Do something different on overscroll - offsetChildrenTopAndBottom() trackMotionScroll(delta, delta); // Check to see if we have bumped into the scroll limit Loading @@ -2373,7 +2508,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Check if the top of the motion view is where it is // supposed to be if (motionView.getTop() != mMotionViewNewTop) { more = false; float vel = scroller.getCurrVelocity(); if (delta > 0) { vel = -vel; } startOverfling(Math.round(vel)); break; } } Loading @@ -2383,6 +2523,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te post(this); } else { endFling(); if (PROFILE_FLINGING) { if (mFlingProfilingStarted) { Debug.stopMethodTracing(); Loading @@ -2390,6 +2531,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } break; } case TOUCH_MODE_OVERFLING: { final OverScroller scroller = mScroller; if (scroller.computeScrollOffset()) { mScrollY = scroller.getCurrY(); invalidate(); post(this); } else { endFling(); } break; } } } } Loading core/java/android/widget/OverScroller.java +11 −0 Original line number Diff line number Diff line Loading @@ -383,4 +383,15 @@ public class OverScroller { public int getFinalY() { return mCurrScroller.getFinalY(); } /** * @hide * Returns the current velocity. * * @return The original velocity less the deceleration. Result may be * negative. */ public float getCurrVelocity() { return mCurrScroller.getCurrVelocity(); } } Loading
core/java/android/widget/AbsListView.java +268 −111 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.widget; import com.android.internal.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; Loading @@ -30,6 +32,7 @@ import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; Loading @@ -41,14 +44,12 @@ import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.ContextMenu.ContextMenuInfo; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionWrapper; import android.view.inputmethod.InputMethodManager; import android.view.ContextMenu.ContextMenuInfo; import com.android.internal.R; import java.util.ArrayList; import java.util.List; Loading Loading @@ -126,6 +127,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> 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 */ Loading Loading @@ -1915,6 +1927,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLayoutMode = LAYOUT_NORMAL; layoutChildren(); } } else { int touchMode = mTouchMode; if (touchMode == TOUCH_MODE_OVERSCROLL || touchMode == TOUCH_MODE_OVERFLING) { mScrollY = 0; if (mFlingRunnable != null) { mFlingRunnable.endFling(); } } } } Loading Loading @@ -1947,6 +1967,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te switch (action) { case MotionEvent.ACTION_DOWN: { switch (mTouchMode) { case TOUCH_MODE_OVERFLING: { mFlingRunnable.endFling(); mTouchMode = TOUCH_MODE_OVERSCROLL; mLastY = y; mMotionCorrection = 0; break; } default: { int motionPosition = pointToPosition(x, y); if (!mDataChanged) { if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0) Loading Loading @@ -1986,6 +2016,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLastY = Integer.MIN_VALUE; break; } } break; } case MotionEvent.ACTION_MOVE: { deltaY = y - mMotionY; Loading @@ -2008,17 +2041,61 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (y != mLastY) { deltaY -= mMotionCorrection; int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY; int motionViewPrevTop = 0; View motionView = this.getChildAt(mMotionPosition - mFirstPosition); if (motionView != null) { motionViewPrevTop = motionView.getTop(); } // No need to do all this work if we're not going to move anyway if (incrementalDeltaY != 0) { trackMotionScroll(deltaY, incrementalDeltaY); } // Check to see if we have bumped into the scroll limit View motionView = this.getChildAt(mMotionPosition - mFirstPosition); motionView = this.getChildAt(mMotionPosition - mFirstPosition); if (motionView != null) { // Check if the top of the motion view is where it is // supposed to be if (motionView.getTop() != mMotionViewNewTop) { final int motionViewRealTop = motionView.getTop(); final int motionViewNewTop = mMotionViewNewTop; if (motionViewRealTop != motionViewNewTop) { // Apply overscroll mScrollY -= incrementalDeltaY - (motionViewRealTop - motionViewPrevTop); 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) { int topOffset = motionView.getTop() - mMotionViewNewTop; 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 = findMotionRow(y); Loading @@ -2029,6 +2106,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mMotionY = y; mMotionPosition = motionPosition; } } else { mScrollY -= incrementalDeltaY; invalidate(); } mLastY = y; } Loading Loading @@ -2120,6 +2200,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mFlingRunnable = new FlingRunnable(); } reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); mFlingRunnable.start(-initialVelocity); } else { mTouchMode = TOUCH_MODE_REST; Loading @@ -2130,6 +2211,24 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mTouchMode = TOUCH_MODE_REST; 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(); reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); if (Math.abs(initialVelocity) > mMinimumVelocity) { mFlingRunnable.startOverfling(-initialVelocity); } else { mFlingRunnable.startSpringback(); } break; } setPressed(false); Loading Loading @@ -2157,6 +2256,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> 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); Loading @@ -2175,7 +2287,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mVelocityTracker = null; } } } } return true; Loading Loading @@ -2205,8 +2317,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te switch (action) { case MotionEvent.ACTION_DOWN: { int touchMode = mTouchMode; if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) { return true; } int motionPosition = findMotionRow(y); if (mTouchMode != TOUCH_MODE_FLING && motionPosition >= 0) { if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) { // User clicked on an actual view (and was not stopping a fling). // Remember where the motion event started v = getChildAt(motionPosition - mFirstPosition); Loading @@ -2218,7 +2335,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te clearScrollingCache(); } mLastY = Integer.MIN_VALUE; if (mTouchMode == TOUCH_MODE_FLING) { if (touchMode == TOUCH_MODE_FLING) { return true; } break; Loading Loading @@ -2293,18 +2410,18 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te /** * Tracks the decay of a fling scroll */ private Scroller mScroller; private OverScroller mScroller; /** * Y value reported by mScroller on the previous fling */ private int mLastFlingY; public FlingRunnable() { mScroller = new Scroller(getContext()); FlingRunnable() { mScroller = new OverScroller(getContext()); } public void start(int initialVelocity) { void start(int initialVelocity) { int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0; mLastFlingY = initialY; mScroller.fling(0, initialY, 0, initialVelocity, Loading @@ -2320,23 +2437,40 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } void startSpringback() { if (mScroller.springback(0, mScrollY, 0, 0, 0, 0)) { mTouchMode = TOUCH_MODE_OVERFLING; invalidate(); post(this); } } void startOverfling(int initialVelocity) { mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, 0, 0, 0, getHeight()); mTouchMode = TOUCH_MODE_OVERFLING; invalidate(); post(this); } private void endFling() { mTouchMode = TOUCH_MODE_REST; reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); clearScrollingCache(); removeCallbacks(this); } public void run() { if (mTouchMode != TOUCH_MODE_FLING) { switch (mTouchMode) { default: return; } case TOUCH_MODE_FLING: { if (mItemCount == 0 || getChildCount() == 0) { endFling(); return; } final Scroller scroller = mScroller; final OverScroller scroller = mScroller; boolean more = scroller.computeScrollOffset(); final int y = scroller.getCurrY(); Loading Loading @@ -2365,6 +2499,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta); } // Do something different on overscroll - offsetChildrenTopAndBottom() trackMotionScroll(delta, delta); // Check to see if we have bumped into the scroll limit Loading @@ -2373,7 +2508,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Check if the top of the motion view is where it is // supposed to be if (motionView.getTop() != mMotionViewNewTop) { more = false; float vel = scroller.getCurrVelocity(); if (delta > 0) { vel = -vel; } startOverfling(Math.round(vel)); break; } } Loading @@ -2383,6 +2523,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te post(this); } else { endFling(); if (PROFILE_FLINGING) { if (mFlingProfilingStarted) { Debug.stopMethodTracing(); Loading @@ -2390,6 +2531,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } break; } case TOUCH_MODE_OVERFLING: { final OverScroller scroller = mScroller; if (scroller.computeScrollOffset()) { mScrollY = scroller.getCurrY(); invalidate(); post(this); } else { endFling(); } break; } } } } Loading
core/java/android/widget/OverScroller.java +11 −0 Original line number Diff line number Diff line Loading @@ -383,4 +383,15 @@ public class OverScroller { public int getFinalY() { return mCurrScroller.getFinalY(); } /** * @hide * Returns the current velocity. * * @return The original velocity less the deceleration. Result may be * negative. */ public float getCurrVelocity() { return mCurrScroller.getCurrVelocity(); } }