BottomSheetBehavior.java revision 452f94d1c58e2a7476019b98c3bf0e4b322d1525
1452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki/* 2452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Copyright (C) 2015 The Android Open Source Project 3452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 4452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Licensed under the Apache License, Version 2.0 (the "License"); 5452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * you may not use this file except in compliance with the License. 6452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * You may obtain a copy of the License at 7452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 8452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * http://www.apache.org/licenses/LICENSE-2.0 9452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 10452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Unless required by applicable law or agreed to in writing, software 11452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * distributed under the License is distributed on an "AS IS" BASIS, 12452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * See the License for the specific language governing permissions and 14452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * limitations under the License. 15452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 16452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 17452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakipackage android.support.design.widget; 18452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 19452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.content.Context; 20452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.content.res.TypedArray; 21452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.os.Parcel; 22452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.os.Parcelable; 23452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.annotation.IntDef; 24452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.design.R; 25452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.view.MotionEventCompat; 26452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.view.ViewCompat; 27452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.widget.ViewDragHelper; 28452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.util.AttributeSet; 29452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.MotionEvent; 30452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.View; 31452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.ViewGroup; 32452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 33452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.annotation.Retention; 34452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.annotation.RetentionPolicy; 35452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.ref.WeakReference; 36452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 37452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 38452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki/** 39452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * An interaction behavior plugin for a child view of {@link CoordinatorLayout} to make it work as 40452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * a bottom sheet. 41452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 42452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakipublic class BottomSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> { 43452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 44452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 45452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is dragging. 46452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 47452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_DRAGGING = 1; 48452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 49452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 50452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is settling. 51452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 52452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_SETTLING = 2; 53452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 54452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 55452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is expanded. 56452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 57452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_EXPANDED = 3; 58452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 59452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 60452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is collapsed. 61452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 62452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_COLLAPSED = 4; 63452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 64452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** @hide */ 65452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @IntDef({STATE_EXPANDED, STATE_COLLAPSED, STATE_DRAGGING, STATE_SETTLING}) 66452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Retention(RetentionPolicy.SOURCE) 67452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public @interface State {} 68452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 69452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mPeekHeight; 70452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 71452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mMinOffset; 72452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 73452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mMaxOffset; 74452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 75452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 76452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mState = STATE_COLLAPSED; 77452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 78452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private ViewDragHelper mViewDragHelper; 79452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 80452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private boolean mIgnoreEvents; 81452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 82452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mLastNestedScrollDy; 83452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 84452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mParentHeight; 85452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 86452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private WeakReference<V> mViewRef; 87452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 88452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 89452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Default constructor for instantiating BottomSheetBehaviors. 90452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 91452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public BottomSheetBehavior() { 92452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 93452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 94452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 95452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Default constructor for inflating BottomSheetBehaviors from layout. 96452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 97452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param context The {@link Context}. 98452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param attrs The {@link AttributeSet}. 99452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 100452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public BottomSheetBehavior(Context context, AttributeSet attrs) { 101452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super(context, attrs); 102452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki TypedArray a = context.obtainStyledAttributes(attrs, 103452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki R.styleable.BottomSheetBehavior_Params); 104452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setPeekHeight(a.getDimensionPixelSize( 105452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki R.styleable.BottomSheetBehavior_Params_behavior_peekHeight, 0)); 106452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki a.recycle(); 107452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 108452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 109452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 110452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) { 111452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return new SavedState(super.onSaveInstanceState(parent, child), mState); 112452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 113452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 114452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 115452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) { 116452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki SavedState ss = (SavedState) state; 117452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super.onRestoreInstanceState(parent, child, ss.getSuperState()); 118452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mState = ss.state; 119452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Intermediate states are restored as collapsed state 120452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mState == STATE_DRAGGING || mState == STATE_SETTLING) { 121452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mState = STATE_COLLAPSED; 122452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 123452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 124452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 125452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 126452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) { 127452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // First let the parent lay it out 128452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki parent.onLayoutChild(child, layoutDirection); 129452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Offset the bottom sheet 130452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mParentHeight = parent.getHeight(); 131452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mMinOffset = Math.max(0, mParentHeight - child.getHeight()); 132452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mMaxOffset = mParentHeight - mPeekHeight; 133452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mState == STATE_EXPANDED) { 134452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.offsetTopAndBottom(child, mMinOffset); 135452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 136452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.offsetTopAndBottom(child, mMaxOffset); 137452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mState = STATE_COLLAPSED; 138452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 139452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper == null) { 140452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewDragHelper = ViewDragHelper.create(parent, mDragCallback); 141452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 142452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewRef = new WeakReference<>(child); 143452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return true; 144452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 145452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 146452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 147452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { 148452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int action = MotionEventCompat.getActionMasked(event); 149452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki switch (action) { 150452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_UP: 151452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_CANCEL: 152452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Reset the ignore flag 153452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mIgnoreEvents) { 154452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mIgnoreEvents = false; 155452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return false; 156452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 157452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki break; 158452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_DOWN: 159452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mIgnoreEvents = !parent.isPointInChildBounds(child, 160452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki (int) event.getX(), (int) event.getY()); 161452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki break; 162452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 163452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return !mIgnoreEvents && mViewDragHelper.shouldInterceptTouchEvent(event); 164452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 165452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 166452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 167452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { 168452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewDragHelper.processTouchEvent(event); 169452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return true; 170452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 171452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 172452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 173452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, 174452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki View directTargetChild, View target, int nestedScrollAxes) { 175452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mLastNestedScrollDy = 0; 176452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; 177452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 178452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 179452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 180452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, 181452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int dy, int[] consumed) { 182452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int currentTop = child.getTop(); 183452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int newTop = currentTop - dy; 184452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (dy > 0) { // Scrolling up 185452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (newTop < mMinOffset) { 186452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki consumed[1] = currentTop - mMinOffset; 187452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki child.offsetTopAndBottom(-consumed[1]); 188452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_EXPANDED); 189452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 190452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki consumed[1] = dy; 191452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki child.offsetTopAndBottom(-dy); 192452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_DRAGGING); 193452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 194452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else if (dy < 0) { // Scrolling down 195452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (!ViewCompat.canScrollVertically(target, -1)) { 196452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (newTop > mMaxOffset) { 197452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki consumed[1] = currentTop - mMaxOffset; 198452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki child.offsetTopAndBottom(-consumed[1]); 199452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_COLLAPSED); 200452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 201452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki consumed[1] = dy; 202452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki child.offsetTopAndBottom(-dy); 203452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_DRAGGING); 204452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 205452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 206452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 207452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mLastNestedScrollDy = dy; 208452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 209452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 210452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 211452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { 212452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mLastNestedScrollDy == 0 || child.getTop() == mMinOffset) { 213452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return; 214452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 215452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int top; 216452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int targetState; 217452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mLastNestedScrollDy > 0) { 218452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMinOffset; 219452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki targetState = STATE_EXPANDED; 220452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 221452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMaxOffset; 222452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki targetState = STATE_COLLAPSED; 223452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 224452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_SETTLING); 225452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) { 226452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState)); 227452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 228452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 229452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 230452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 231452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Sets the height of the bottom sheet when it is collapsed. 232452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 233452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param peekHeight The height of the collapsed bottom sheet in pixels. 234452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_peekHeight 235452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 236452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final void setPeekHeight(int peekHeight) { 237452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mPeekHeight = Math.max(0, peekHeight); 238452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mMaxOffset = mParentHeight - peekHeight; 239452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 240452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 241452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 242452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Gets the height of the bottom sheet when it is collapsed. 243452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 244452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @return The height of the collapsed bottom sheet. 245452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_peekHeight 246452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 247452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final int getPeekHeight() { 248452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return mPeekHeight; 249452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 250452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 251452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 252452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Sets the state of the bottom sheet. The bottom sheet will transition to that state with 253452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * animation. 254452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 255452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param state Either {@link #STATE_COLLAPSED} or {@link #STATE_EXPANDED}. 256452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 257452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final void setState(@State int state) { 258452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki V child = mViewRef.get(); 259452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (child == null) { 260452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return; 261452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 262452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int top; 263452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (state == STATE_COLLAPSED) { 264452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMaxOffset; 265452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else if (state == STATE_EXPANDED) { 266452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMinOffset; 267452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 268452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki throw new IllegalArgumentException("Illegal state argument: " + state); 269452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 270452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_SETTLING); 271452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) { 272452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(child, new SettleRunnable(child, state)); 273452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 274452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 275452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 276452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 277452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Gets the current state of the bottom sheet. 278452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 279452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @return One of {@link #STATE_EXPANDED}, {@link #STATE_COLLAPSED}, {@link #STATE_DRAGGING}, 280452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * and {@link #STATE_SETTLING}. 281452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 282452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 283452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final int getState() { 284452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return mState; 285452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 286452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 287452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private void setStateInternal(@State int state) { 288452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mState == state) { 289452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return; 290452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 291452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mState = state; 292452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // TODO: Invoke listeners. 293452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 294452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 295452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() { 296452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 297452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 298452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean tryCaptureView(View child, int pointerId) { 299452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return true; 300452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 301452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 302452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 303452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onViewDragStateChanged(int state) { 304452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (state == ViewDragHelper.STATE_DRAGGING) { 305452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_DRAGGING); 306452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 307452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 308452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 309452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 310452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onViewReleased(View releasedChild, float xvel, float yvel) { 311452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int top; 312452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State int targetState; 313452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (yvel < 0) { 314452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMinOffset; 315452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki targetState = STATE_EXPANDED; 316452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 317452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMaxOffset; 318452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki targetState = STATE_COLLAPSED; 319452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 320452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_SETTLING); 321452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) { 322452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(releasedChild, 323452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki new SettleRunnable(releasedChild, targetState)); 324452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 325452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 326452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 327452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 328452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public int clampViewPositionVertical(View child, int top, int dy) { 329452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return MathUtils.constrain(top, mMinOffset, mMaxOffset); 330452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 331452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 332452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 333452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public int clampViewPositionHorizontal(View child, int left, int dx) { 334452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return child.getLeft(); 335452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 336452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki }; 337452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 338452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private class SettleRunnable implements Runnable { 339452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 340452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final View mView; 341452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 342452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 343452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final int mTargetState; 344452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 345452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki SettleRunnable(View view, @State int targetState) { 346452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mView = view; 347452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mTargetState = targetState; 348452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 349452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 350452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 351452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void run() { 352452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) { 353452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(mView, this); 354452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 355452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(mTargetState); 356452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 357452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 358452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 359452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 360452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki protected static class SavedState extends View.BaseSavedState { 361452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 362452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 363452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki final int state; 364452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 365452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState(Parcel source) { 366452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super(source); 367452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki //noinspection ResourceType 368452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki state = source.readInt(); 369452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 370452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 371452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState(Parcelable superState, @State int state) { 372452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super(superState); 373452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki this.state = state; 374452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 375452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 376452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 377452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void writeToParcel(Parcel out, int flags) { 378452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super.writeToParcel(out, flags); 379452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki out.writeInt(state); 380452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 381452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 382452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final Parcelable.Creator<SavedState> CREATOR = 383452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki new Parcelable.Creator<SavedState>() { 384452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 385452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState createFromParcel(Parcel source) { 386452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return new SavedState(source); 387452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 388452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 389452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 390452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState[] newArray(int size) { 391452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return new SavedState[size]; 392452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 393452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki }; 394452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 395452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 396452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /* 397452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * A utility function to get the {@link BottomSheetBehavior} associated with the {@code view}. 398452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 399452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param view The {@link View} with {@link BottomSheetBehavior}. 400452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @return The {@link BottomSheetBehavior} associated with the {@code view}. 401452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 402452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @SuppressWarnings("unchecked") 403452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static <V extends View> BottomSheetBehavior<V> from(V view) { 404452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewGroup.LayoutParams params = view.getLayoutParams(); 405452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (!(params instanceof CoordinatorLayout.LayoutParams)) { 406452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki throw new IllegalArgumentException("The view is not a child of CoordinatorLayout"); 407452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 408452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params) 409452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki .getBehavior(); 410452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (!(behavior instanceof BottomSheetBehavior)) { 411452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki throw new IllegalArgumentException( 412452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki "The view is not associated with BottomSheetBehavior"); 413452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 414452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return (BottomSheetBehavior<V>) behavior; 415452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 416452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 417452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki} 418