Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96d62aff authored by Adam Powell's avatar Adam Powell
Browse files

ListViews in ScrollViews and ScrollViews in ListViews

Dogs and cats, living together, mass hysteria!

Add support for nested scrolling to AbsListView, granting the ability
to both ListView and GridView.

Change-Id: Iae5c0518c5cebe41927720a7a27916ef13ca706e
parent 5b8b9200
Loading
Loading
Loading
Loading
+109 −36
Original line number Diff line number Diff line
@@ -611,6 +611,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te

    final boolean[] mIsScrap = new boolean[1];

    private final int[] mScrollOffset = new int[2];
    private final int[] mScrollConsumed = new int[2];

    // True when the popup should be hidden because of a call to
    // dispatchDisplayHint()
    private boolean mPopupHidden;
@@ -3264,13 +3267,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        }
    }

    private boolean startScrollIfNeeded(int y) {
    private boolean startScrollIfNeeded(int y, MotionEvent vtev) {
        // Check if we have moved far enough that it looks more like a
        // scroll than a tap
        final int deltaY = y - mMotionY;
        final int distance = Math.abs(deltaY);
        final boolean overscroll = mScrollY != 0;
        if (overscroll || distance > mTouchSlop) {
        if ((overscroll || distance > mTouchSlop) &&
                (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {
            createScrollingCache();
            if (overscroll) {
                mTouchMode = TOUCH_MODE_OVERSCROLL;
@@ -3292,17 +3296,28 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            if (parent != null) {
                parent.requestDisallowInterceptTouchEvent(true);
            }
            scrollIfNeeded(y);
            scrollIfNeeded(y, vtev);
            return true;
        }

        return false;
    }

    private void scrollIfNeeded(int y) {
        final int rawDeltaY = y - mMotionY;
    private void scrollIfNeeded(int y, MotionEvent vtev) {
        int rawDeltaY = y - mMotionY;
        if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) {
            rawDeltaY -= mScrollConsumed[1];
            mMotionCorrection -= mScrollOffset[1];
            if (mLastY != Integer.MIN_VALUE) {
                mLastY -= mScrollOffset[1] + mScrollConsumed[1];
            }
            if (vtev != null) {
                vtev.offsetLocation(0, mScrollOffset[1]);
            }
        }
        final int deltaY = rawDeltaY - mMotionCorrection;
        int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
        int lastYCorrection = 0;

        if (mTouchMode == TOUCH_MODE_SCROLL) {
            if (PROFILE_SCROLLING) {
@@ -3361,6 +3376,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te

                        int overscroll = -incrementalDeltaY -
                                (motionViewRealTop - motionViewPrevTop);
                        if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll,
                                mScrollOffset)) {
                            mMotionCorrection -= mScrollOffset[1];
                            lastYCorrection -= mScrollOffset[1];
                            vtev.offsetLocation(0, mScrollOffset[1]);
                        } else {
                            overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
                                    0, mOverscrollDistance, true);
                            if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
@@ -3376,13 +3397,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                                            !contentFits())) {
                                mDirection = 0; // Reset when entering overscroll.
                                mTouchMode = TOUCH_MODE_OVERSCROLL;
                            if (rawDeltaY > 0) {
                                if (deltaY > 0) {
                                    mEdgeGlowTop.onPull((float) overscroll / getHeight());
                                    if (!mEdgeGlowBottom.isFinished()) {
                                        mEdgeGlowBottom.onRelease();
                                    }
                                    invalidate(mEdgeGlowTop.getBounds(false));
                            } else if (rawDeltaY < 0) {
                                } else if (deltaY < 0) {
                                    mEdgeGlowBottom.onPull((float) overscroll / getHeight());
                                    if (!mEdgeGlowTop.isFinished()) {
                                        mEdgeGlowTop.onRelease();
@@ -3391,9 +3412,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                                }
                            }
                        }
                    }
                    mMotionY = y;
                }
                mLastY = y;
                mLastY = y + lastYCorrection;
            }
        } else if (mTouchMode == TOUCH_MODE_OVERSCROLL) {
            if (y != mLastY) {
@@ -3517,6 +3539,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            return false;
        }

        startNestedScroll(SCROLL_AXIS_VERTICAL);

        if (mFastScroll != null) {
            boolean intercepted = mFastScroll.onTouchEvent(ev);
            if (intercepted) {
@@ -3525,7 +3549,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        }

        initVelocityTrackerIfNotExists();
        mVelocityTracker.addMovement(ev);
        final MotionEvent vtev = MotionEvent.obtain(ev);

        final int actionMasked = ev.getActionMasked();
        switch (actionMasked) {
@@ -3535,7 +3559,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            }

            case MotionEvent.ACTION_MOVE: {
                onTouchMove(ev);
                onTouchMove(ev, vtev);
                break;
            }

@@ -3586,6 +3610,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            }
        }

        if (mVelocityTracker != null) {
            mVelocityTracker.addMovement(vtev);
        }
        vtev.recycle();
        return true;
    }

@@ -3652,7 +3680,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        }
    }

    private void onTouchMove(MotionEvent ev) {
    private void onTouchMove(MotionEvent ev, MotionEvent vtev) {
        int pointerIndex = ev.findPointerIndex(mActivePointerId);
        if (pointerIndex == -1) {
            pointerIndex = 0;
@@ -3673,7 +3701,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            case TOUCH_MODE_DONE_WAITING:
                // Check if we have moved far enough that it looks more like a
                // scroll than a tap. If so, we'll enter scrolling mode.
                if (startScrollIfNeeded(y)) {
                if (startScrollIfNeeded(y, vtev)) {
                    break;
                }
                // Otherwise, check containment within list bounds. If we're
@@ -3693,7 +3721,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                break;
            case TOUCH_MODE_SCROLL:
            case TOUCH_MODE_OVERSCROLL:
                scrollIfNeeded(y);
                scrollIfNeeded(y, vtev);
                break;
        }
    }
@@ -3934,6 +3962,49 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        return super.onGenericMotionEvent(event);
    }

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        return ((nestedScrollAxes & SCROLL_AXIS_VERTICAL) != 0);
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int axes) {
        super.onNestedScrollAccepted(child, target, axes);
        startNestedScroll(SCROLL_AXIS_VERTICAL);
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
            int dxUnconsumed, int dyUnconsumed) {
        final int motionIndex = getChildCount() / 2;
        final View motionView = getChildAt(motionIndex);
        final int oldTop = motionView != null ? motionView.getTop() : 0;
        if (motionView == null || trackMotionScroll(-dyUnconsumed, -dyUnconsumed)) {
            int myUnconsumed = dyUnconsumed;
            int myConsumed = 0;
            if (motionView != null) {
                myConsumed = motionView.getTop() - oldTop;
                myUnconsumed -= myConsumed;
            }
            dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null);
        }
    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        final int childCount = getChildCount();
        if (!consumed && childCount > 0 && canScrollList((int) velocityY) &&
                Math.abs(velocityY) > mMinimumVelocity) {
            reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
            if (mFlingRunnable == null) {
                mFlingRunnable = new FlingRunnable();
            }
            mFlingRunnable.start((int) velocityY);
            return true;
        }
        return dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
@@ -4070,6 +4141,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            mLastY = Integer.MIN_VALUE;
            initOrResetVelocityTracker();
            mVelocityTracker.addMovement(ev);
            startNestedScroll(SCROLL_AXIS_VERTICAL);
            if (touchMode == TOUCH_MODE_FLING) {
                return true;
            }
@@ -4087,7 +4159,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                final int y = (int) ev.getY(pointerIndex);
                initVelocityTrackerIfNotExists();
                mVelocityTracker.addMovement(ev);
                if (startScrollIfNeeded(y)) {
                if (startScrollIfNeeded(y, null)) {
                    return true;
                }
                break;
@@ -4101,6 +4173,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            mActivePointerId = INVALID_POINTER;
            recycleVelocityTracker();
            reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
            stopNestedScroll();
            break;
        }

+9 −1
Original line number Diff line number Diff line
@@ -583,7 +583,8 @@ public class ScrollView extends FrameLayout {
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        initVelocityTrackerIfNotExists();
        mVelocityTracker.addMovement(ev);

        MotionEvent vtev = MotionEvent.obtain(ev);

        final int action = ev.getAction();

@@ -628,6 +629,7 @@ public class ScrollView extends FrameLayout {
                int deltaY = mLastMotionY - y;
                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
                    deltaY -= mScrollConsumed[1] + mScrollOffset[1];
                    vtev.offsetLocation(0, mScrollOffset[1]);
                }
                if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
                    final ViewParent parent = getParent();
@@ -663,6 +665,7 @@ public class ScrollView extends FrameLayout {
                    final int unconsumedY = deltaY - scrolledDeltaY;
                    if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {
                        mLastMotionY -= mScrollOffset[1];
                        vtev.offsetLocation(0, mScrollOffset[1]);
                    } else if (canOverscroll) {
                        final int pulledToY = oldY + deltaY;
                        if (pulledToY < 0) {
@@ -720,6 +723,11 @@ public class ScrollView extends FrameLayout {
                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
                break;
        }

        if (mVelocityTracker != null) {
            mVelocityTracker.addMovement(vtev);
        }
        vtev.recycle();
        return true;
    }