[go: nahoru, domu]

1ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor/*
2ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor * Copyright (C) 2015 The Android Open Source Project
3ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor *
4ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor * Licensed under the Apache License, Version 2.0 (the "License");
5ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor * you may not use this file except in compliance with the License.
6ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor * You may obtain a copy of the License at
7ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor *
8ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor *      http://www.apache.org/licenses/LICENSE-2.0
9ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor *
10ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor * Unless required by applicable law or agreed to in writing, software
11ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor * distributed under the License is distributed on an "AS IS" BASIS,
12ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor * See the License for the specific language governing permissions and
14ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor * limitations under the License.
15ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor */
16ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
17ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellorpackage android.support.design.widget;
18ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
19ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellorimport android.content.Context;
20ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellorimport android.support.design.widget.CoordinatorLayout.Behavior;
2172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.v4.view.MotionEventCompat;
2272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.v4.view.VelocityTrackerCompat;
23ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellorimport android.support.v4.view.ViewCompat;
24ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellorimport android.support.v4.widget.ScrollerCompat;
25ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellorimport android.util.AttributeSet;
2672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.view.MotionEvent;
2772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.view.VelocityTracker;
28ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellorimport android.view.View;
2972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.view.ViewConfiguration;
30ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
31ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor/**
32ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor * The {@link Behavior} for a view that sits vertically above scrolling a view.
33ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor * See {@link HeaderScrollingViewBehavior}.
34ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor */
35ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellorabstract class HeaderBehavior<V extends View> extends ViewOffsetBehavior<V> {
36ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
3772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    private static final int INVALID_POINTER = -1;
3872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
39ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    private Runnable mFlingRunnable;
40ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    private ScrollerCompat mScroller;
41ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
4272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    private boolean mIsBeingDragged;
4372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    private int mActivePointerId = INVALID_POINTER;
4472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    private int mLastMotionY;
4572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    private int mTouchSlop = -1;
4672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    private VelocityTracker mVelocityTracker;
4772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
4872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    public HeaderBehavior() {}
49ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
50ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    public HeaderBehavior(Context context, AttributeSet attrs) {
51ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        super(context, attrs);
52ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    }
53ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
5472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    @Override
5572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
5672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        if (mTouchSlop < 0) {
5772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            mTouchSlop = ViewConfiguration.get(parent.getContext()).getScaledTouchSlop();
5872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
5972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
6072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        final int action = ev.getAction();
6172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
6272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        // Shortcut since we're being dragged
6372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        if (action == MotionEvent.ACTION_MOVE && mIsBeingDragged) {
6472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            return true;
6572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
6672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
6772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        switch (MotionEventCompat.getActionMasked(ev)) {
6872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            case MotionEvent.ACTION_DOWN: {
6972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                mIsBeingDragged = false;
7072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final int x = (int) ev.getX();
7172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final int y = (int) ev.getY();
7272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                if (canDragView(child) && parent.isPointInChildBounds(child, x, y)) {
7372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mLastMotionY = y;
7472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
7572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    ensureVelocityTracker();
7672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                }
7772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                break;
7872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            }
7972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
8072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            case MotionEvent.ACTION_MOVE: {
8172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final int activePointerId = mActivePointerId;
8272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                if (activePointerId == INVALID_POINTER) {
8372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    // If we don't have a valid id, the touch down wasn't on content.
8472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    break;
8572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                }
8672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
8772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                if (pointerIndex == -1) {
8872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    break;
8972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                }
9072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
9172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final int y = (int) MotionEventCompat.getY(ev, pointerIndex);
9272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final int yDiff = Math.abs(y - mLastMotionY);
9372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                if (yDiff > mTouchSlop) {
9472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mIsBeingDragged = true;
9572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mLastMotionY = y;
9672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                }
9772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                break;
9872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            }
9972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
10072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            case MotionEvent.ACTION_CANCEL:
10172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            case MotionEvent.ACTION_UP: {
10272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                mIsBeingDragged = false;
10372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                mActivePointerId = INVALID_POINTER;
10472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                if (mVelocityTracker != null) {
10572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mVelocityTracker.recycle();
10672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mVelocityTracker = null;
10772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                }
10872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                break;
10972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            }
11072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
11172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
11272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        if (mVelocityTracker != null) {
11372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            mVelocityTracker.addMovement(ev);
11472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
11572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
11672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        return mIsBeingDragged;
11772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    }
11872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
11972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    @Override
12072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
12172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        if (mTouchSlop < 0) {
12272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            mTouchSlop = ViewConfiguration.get(parent.getContext()).getScaledTouchSlop();
12372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
12472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
12572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        switch (MotionEventCompat.getActionMasked(ev)) {
12672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            case MotionEvent.ACTION_DOWN: {
12772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final int x = (int) ev.getX();
12872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final int y = (int) ev.getY();
12972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
13072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                if (parent.isPointInChildBounds(child, x, y) && canDragView(child)) {
13172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mLastMotionY = y;
13272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
13372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    ensureVelocityTracker();
13472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                } else {
13572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    return false;
13672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                }
13772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                break;
13872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            }
13972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
14072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            case MotionEvent.ACTION_MOVE: {
14172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final int activePointerIndex = MotionEventCompat.findPointerIndex(ev,
14272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                        mActivePointerId);
14372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                if (activePointerIndex == -1) {
14472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    return false;
14572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                }
14672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
14772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final int y = (int) MotionEventCompat.getY(ev, activePointerIndex);
14872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                int dy = mLastMotionY - y;
14972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
15072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                if (!mIsBeingDragged && Math.abs(dy) > mTouchSlop) {
15172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mIsBeingDragged = true;
15272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    if (dy > 0) {
15372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                        dy -= mTouchSlop;
15472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    } else {
15572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                        dy += mTouchSlop;
15672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    }
15772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                }
15872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
15972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                if (mIsBeingDragged) {
16072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mLastMotionY = y;
16172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    // We're being dragged so scroll the ABL
16272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    scroll(parent, child, dy, getMaxDragOffset(child), 0);
16372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                }
16472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                break;
16572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            }
16672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
16772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            case MotionEvent.ACTION_UP:
16872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                if (mVelocityTracker != null) {
16972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mVelocityTracker.addMovement(ev);
17072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mVelocityTracker.computeCurrentVelocity(1000);
17172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    float yvel = VelocityTrackerCompat.getYVelocity(mVelocityTracker,
17272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                            mActivePointerId);
17372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    fling(parent, child, -getScrollRangeForDragFling(child), 0, yvel);
17472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                }
17572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                // $FALLTHROUGH
17672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            case MotionEvent.ACTION_CANCEL: {
17772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                mIsBeingDragged = false;
17872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                mActivePointerId = INVALID_POINTER;
17972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                if (mVelocityTracker != null) {
18072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mVelocityTracker.recycle();
18172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    mVelocityTracker = null;
18272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                }
18372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                break;
18472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            }
18572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
18672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
18772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        if (mVelocityTracker != null) {
18872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            mVelocityTracker.addMovement(ev);
18972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
19072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
19172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        return true;
19272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    }
19372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
19472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    int setHeaderTopBottomOffset(CoordinatorLayout parent, V header, int newOffset) {
19572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        return setHeaderTopBottomOffset(parent, header, newOffset,
196ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                Integer.MIN_VALUE, Integer.MAX_VALUE);
197ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    }
198ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
19972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    int setHeaderTopBottomOffset(CoordinatorLayout parent, V header, int newOffset,
20072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            int minOffset, int maxOffset) {
201ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        final int curOffset = getTopAndBottomOffset();
202ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        int consumed = 0;
203ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
204ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
205ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            // If we have some scrolling range, and we're currently within the min and max
206ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            // offsets, calculate a new offset
207ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset);
208ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
209ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            if (curOffset != newOffset) {
210ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                setTopAndBottomOffset(newOffset);
211ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                // Update how much dy we have consumed
212ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                consumed = curOffset - newOffset;
213ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            }
214ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        }
215ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
216ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        return consumed;
217ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    }
218ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
21972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    int getTopBottomOffsetForScrollingSibling() {
220ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        return getTopAndBottomOffset();
221ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    }
222ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
22372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    final int scroll(CoordinatorLayout coordinatorLayout, V header,
224ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            int dy, int minOffset, int maxOffset) {
225ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        return setHeaderTopBottomOffset(coordinatorLayout, header,
226ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                getTopBottomOffsetForScrollingSibling() - dy, minOffset, maxOffset);
227ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    }
228ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
22972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    final boolean fling(CoordinatorLayout coordinatorLayout, V layout, int minOffset,
230ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            int maxOffset, float velocityY) {
231ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        if (mFlingRunnable != null) {
232ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            layout.removeCallbacks(mFlingRunnable);
23372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            mFlingRunnable = null;
234ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        }
235ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
236ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        if (mScroller == null) {
237ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            mScroller = ScrollerCompat.create(layout.getContext());
238ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        }
239ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
240ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        mScroller.fling(
241ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                0, getTopAndBottomOffset(), // curr
242ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                0, Math.round(velocityY), // velocity.
243ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                0, 0, // x
244ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                minOffset, maxOffset); // y
245ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
246ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        if (mScroller.computeScrollOffset()) {
247ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            mFlingRunnable = new FlingRunnable(coordinatorLayout, layout);
248ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            ViewCompat.postOnAnimation(layout, mFlingRunnable);
249ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            return true;
25047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        } else {
25147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            onFlingFinished(coordinatorLayout, layout);
25247543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            return false;
25372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
25447543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes    }
25547543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes
25647543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes    /**
25747543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes     * Called when a fling has finished, or the fling was initiated but there wasn't enough
25847543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes     * velocity to start it.
25947543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes     */
26047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes    void onFlingFinished(CoordinatorLayout parent, V layout) {
26147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        // no-op
26272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    }
26372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
26472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    /**
26572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes     * Return true if the view can be dragged.
26672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes     */
26772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    boolean canDragView(V view) {
26872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        return false;
26972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    }
27072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
27172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    /**
27272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes     * Returns the maximum px offset when {@code view} is being dragged.
27372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes     */
27472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    int getMaxDragOffset(V view) {
27572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        return -view.getHeight();
27672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    }
27772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
27872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    int getScrollRangeForDragFling(V view) {
27972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        return view.getHeight();
28072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    }
28172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
28272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes    private void ensureVelocityTracker() {
28372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        if (mVelocityTracker == null) {
28472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            mVelocityTracker = VelocityTracker.obtain();
285ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        }
286ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    }
287ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
288ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    private class FlingRunnable implements Runnable {
289ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        private final CoordinatorLayout mParent;
29072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        private final V mLayout;
291ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
29272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        FlingRunnable(CoordinatorLayout parent, V layout) {
293ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            mParent = parent;
294ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            mLayout = layout;
295ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        }
296ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
297ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
298ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        public void run() {
29947543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            if (mLayout != null && mScroller != null) {
30047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                if (mScroller.computeScrollOffset()) {
30147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    setHeaderTopBottomOffset(mParent, mLayout, mScroller.getCurrY());
30247543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    // Post ourselves so that we run on the next animation
30347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    ViewCompat.postOnAnimation(mLayout, this);
30447543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                } else {
30547543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    onFlingFinished(mParent, mLayout);
30647543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                }
307ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            }
308ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        }
309ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    }
310ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor}
311