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