BottomSheetBehavior.java revision e3e7063749d095bba8743cad0aeb7164a567890d
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; 24dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Arakiimport android.support.annotation.NonNull; 25452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.design.R; 26452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.view.MotionEventCompat; 279aec720809a123c6193304730acf8b55d6ce5a7aYuichi Arakiimport android.support.v4.view.NestedScrollingChild; 28e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.support.v4.view.VelocityTrackerCompat; 29452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.view.ViewCompat; 30452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.widget.ViewDragHelper; 31452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.util.AttributeSet; 32452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.MotionEvent; 33e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.view.VelocityTracker; 34452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.View; 35e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.view.ViewConfiguration; 36452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.ViewGroup; 37452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 38452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.annotation.Retention; 39452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.annotation.RetentionPolicy; 40452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.ref.WeakReference; 41452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 42452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 43452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki/** 44452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * An interaction behavior plugin for a child view of {@link CoordinatorLayout} to make it work as 45452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * a bottom sheet. 46452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 47452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakipublic class BottomSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> { 48452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 49452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 504df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki * Callback for monitoring events about bottom sheets. 51f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 524df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki public abstract static class BottomSheetCallback { 53f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 54f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 55f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Called when the bottom sheet changes its state. 56f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 57dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * @param bottomSheet The bottom sheet view. 58dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * @param newState The new state. This will be one of {@link #STATE_DRAGGING}, 59dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * {@link #STATE_SETTLING}, {@link #STATE_EXPANDED}, 60dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * {@link #STATE_COLLAPSED}, or {@link #STATE_HIDDEN}. 61f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 62dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public abstract void onStateChanged(@NonNull View bottomSheet, @State int newState); 63f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 64f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 65f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Called when the bottom sheet is being dragged. 66f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 67dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * @param bottomSheet The bottom sheet view. 68f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @param slideOffset The new offset of this bottom sheet within its range, from 0 to 1 69f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * when it is moving upward, and from 0 to -1 when it moving downward. 70f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 71dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public abstract void onSlide(@NonNull View bottomSheet, float slideOffset); 72f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 73f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 74f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 75452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is dragging. 76452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 77452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_DRAGGING = 1; 78452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 79452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 80452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is settling. 81452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 82452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_SETTLING = 2; 83452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 84452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 85452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is expanded. 86452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 87452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_EXPANDED = 3; 88452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 89452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 90452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is collapsed. 91452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 92452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_COLLAPSED = 4; 93452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 94f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 95f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * The bottom sheet is hidden. 96f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 97f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public static final int STATE_HIDDEN = 5; 98f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 99452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** @hide */ 100f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki @IntDef({STATE_EXPANDED, STATE_COLLAPSED, STATE_DRAGGING, STATE_SETTLING, STATE_HIDDEN}) 101452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Retention(RetentionPolicy.SOURCE) 102452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public @interface State {} 103452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 104f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private static final float HIDE_THRESHOLD = 0.5f; 105f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 106f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private static final float HIDE_FRICTION = 0.1f; 107f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 108e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private float mMaximumVelocity; 109f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 110452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mPeekHeight; 111452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 112452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mMinOffset; 113452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 114452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mMaxOffset; 115452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 116f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private boolean mHideable; 117f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 118452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 119452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mState = STATE_COLLAPSED; 120452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 121452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private ViewDragHelper mViewDragHelper; 122452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 123452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private boolean mIgnoreEvents; 124452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 125e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private int mLastNestedScrollDy; 126e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 127452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mParentHeight; 128452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 129452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private WeakReference<V> mViewRef; 130452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 1319aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki private WeakReference<View> mNestedScrollingChildRef; 132f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 1339aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki private BottomSheetCallback mCallback; 134f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 135e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private VelocityTracker mVelocityTracker; 136e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 137e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private int mActivePointerId; 138f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 139452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 140452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Default constructor for instantiating BottomSheetBehaviors. 141452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 142452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public BottomSheetBehavior() { 143452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 144452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 145452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 146452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Default constructor for inflating BottomSheetBehaviors from layout. 147452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 148452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param context The {@link Context}. 149452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param attrs The {@link AttributeSet}. 150452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 151452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public BottomSheetBehavior(Context context, AttributeSet attrs) { 152452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super(context, attrs); 153452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki TypedArray a = context.obtainStyledAttributes(attrs, 154452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki R.styleable.BottomSheetBehavior_Params); 155452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setPeekHeight(a.getDimensionPixelSize( 156452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki R.styleable.BottomSheetBehavior_Params_behavior_peekHeight, 0)); 157f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki setHideable(a.getBoolean(R.styleable.BottomSheetBehavior_Params_behavior_hideable, false)); 158452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki a.recycle(); 159e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewConfiguration configuration = ViewConfiguration.get(context); 160e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 161452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 162452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 163452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 164452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) { 165452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return new SavedState(super.onSaveInstanceState(parent, child), mState); 166452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 167452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 168452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 169452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) { 170452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki SavedState ss = (SavedState) state; 171452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super.onRestoreInstanceState(parent, child, ss.getSuperState()); 172452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Intermediate states are restored as collapsed state 173f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (ss.state == STATE_DRAGGING || ss.state == STATE_SETTLING) { 174452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mState = STATE_COLLAPSED; 175f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else { 176f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki mState = ss.state; 177452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 178452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 179452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 180452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 181452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) { 182452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // First let the parent lay it out 183f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (mState != STATE_DRAGGING && mState != STATE_SETTLING) { 184f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki parent.onLayoutChild(child, layoutDirection); 185f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 186452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Offset the bottom sheet 187452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mParentHeight = parent.getHeight(); 188452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mMinOffset = Math.max(0, mParentHeight - child.getHeight()); 189452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mMaxOffset = mParentHeight - mPeekHeight; 190452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mState == STATE_EXPANDED) { 191452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.offsetTopAndBottom(child, mMinOffset); 192f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else if (mHideable && mState == STATE_HIDDEN) { 193f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki ViewCompat.offsetTopAndBottom(child, mParentHeight); 194f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else if (mState == STATE_COLLAPSED) { 195452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.offsetTopAndBottom(child, mMaxOffset); 196452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 197452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper == null) { 198452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewDragHelper = ViewDragHelper.create(parent, mDragCallback); 199452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 200452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewRef = new WeakReference<>(child); 2019aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child)); 202452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return true; 203452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 204452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 205452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 206452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { 20717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki if (!child.isShown()) { 20817fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki return false; 20917fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 210452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int action = MotionEventCompat.getActionMasked(event); 211e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki // Record the velocity 212e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (action == MotionEvent.ACTION_DOWN) { 213e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki reset(); 214e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 215e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mVelocityTracker == null) { 216e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker = VelocityTracker.obtain(); 217e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 218e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.addMovement(event); 219452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki switch (action) { 220452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_UP: 221452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_CANCEL: 222e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mActivePointerId = MotionEvent.INVALID_POINTER_ID; 223452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Reset the ignore flag 224452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mIgnoreEvents) { 225452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mIgnoreEvents = false; 226452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return false; 227452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 228452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki break; 229452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_DOWN: 2309aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki int x = (int) event.getX(); 2319aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki int y = (int) event.getY(); 2329aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki View scroll = mNestedScrollingChildRef.get(); 2339aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (scroll != null && parent.isPointInChildBounds(scroll, x, y)) { 234e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mActivePointerId = event.getPointerId(event.getActionIndex()); 2359aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 236e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mIgnoreEvents = mActivePointerId == MotionEvent.INVALID_POINTER_ID && 2379aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki !parent.isPointInChildBounds(child, x, y); 238452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki break; 239452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 240452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return !mIgnoreEvents && mViewDragHelper.shouldInterceptTouchEvent(event); 241452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 242452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 243452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 244452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { 24517fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki if (!child.isShown()) { 24617fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki return false; 24717fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 248e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int action = MotionEventCompat.getActionMasked(event); 249e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mState == STATE_DRAGGING && action == MotionEvent.ACTION_DOWN) { 250e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return true; 251e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 252452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewDragHelper.processTouchEvent(event); 253e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki // Record the velocity 254e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (action == MotionEvent.ACTION_DOWN) { 255e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki reset(); 256e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 257e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mVelocityTracker == null) { 258e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker = VelocityTracker.obtain(); 259e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 260e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.addMovement(event); 261452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return true; 262452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 263452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 264e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 265e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, 266e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki View directTargetChild, View target, int nestedScrollAxes) { 267e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mLastNestedScrollDy = 0; 268e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; 269e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 270e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 271e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 272e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, 273e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int dy, int[] consumed) { 274e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki View scrollingChild = mNestedScrollingChildRef.get(); 275e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (target != scrollingChild) { 276e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return; 277e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 278e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int currentTop = child.getTop(); 279e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int newTop = currentTop - dy; 280e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (dy > 0) { // Upward 281e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (newTop < mMinOffset) { 282e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = currentTop - mMinOffset; 283e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -consumed[1]); 284e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_EXPANDED); 285e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 286e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = dy; 287e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -dy); 288e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_DRAGGING); 289e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 290e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else if (dy < 0) { // Downward 291e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (!ViewCompat.canScrollVertically(target, -1)) { 292e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (newTop <= mMaxOffset || mHideable) { 293e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = dy; 294e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -dy); 295e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_DRAGGING); 296e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 297e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = currentTop - mMaxOffset; 298e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -consumed[1]); 299e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_COLLAPSED); 300e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 301e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 302e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 303e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki dispatchOnSlide(child.getTop()); 304e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mLastNestedScrollDy = dy; 305e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 306e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 307e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 308e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { 309e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (child.getTop() == mMinOffset || target != mNestedScrollingChildRef.get() || 310e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mLastNestedScrollDy == 0) { 311e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return; 312e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 313e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int top; 314e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int targetState; 315e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mLastNestedScrollDy > 0) { 316e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki top = mMinOffset; 317e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki targetState = STATE_EXPANDED; 318e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else if (mHideable && shouldHide(child, getYVelocity())) { 319e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki top = mParentHeight; 320e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki targetState = STATE_HIDDEN; 321e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 322e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki top = mMaxOffset; 323e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki targetState = STATE_COLLAPSED; 324e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 325e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) { 326e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_SETTLING); 327e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState)); 328e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 329e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(targetState); 330e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 331e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 332e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 333e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 334e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, 335e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki float velocityX, float velocityY) { 336e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return target == mNestedScrollingChildRef.get() && 337e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki (mState != STATE_EXPANDED || 338e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki super.onNestedPreFling(coordinatorLayout, child, target, 339e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki velocityX, velocityY)); 340e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 341e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 342452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 343452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Sets the height of the bottom sheet when it is collapsed. 344452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 345452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param peekHeight The height of the collapsed bottom sheet in pixels. 346452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_peekHeight 347452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 348452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final void setPeekHeight(int peekHeight) { 349452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mPeekHeight = Math.max(0, peekHeight); 350452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mMaxOffset = mParentHeight - peekHeight; 351452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 352452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 353452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 354452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Gets the height of the bottom sheet when it is collapsed. 355452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 356452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @return The height of the collapsed bottom sheet. 357452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_peekHeight 358452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 359452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final int getPeekHeight() { 360452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return mPeekHeight; 361452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 362452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 363452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 364f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Sets whether this bottom sheet can hide when it is swiped down. 365f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 366f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @param hideable {@code true} to make this bottom sheet hideable. 367f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_hideable 368f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 369f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public void setHideable(boolean hideable) { 370f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki mHideable = hideable; 371f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 372f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 373f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 374f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Gets whether this bottom sheet can hide when it is swiped down. 375f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 376f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @return {@code true} if this bottom sheet can hide. 377f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_hideable 378f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 379f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public boolean isHideable() { 380f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return mHideable; 381f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 382f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 383f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 3844df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki * Sets a callback to be notified of bottom sheet events. 385f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 3864df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki * @param callback The callback to notify when bottom sheet events occur. 387f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 3884df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki public void setBottomSheetCallback(BottomSheetCallback callback) { 3894df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki mCallback = callback; 390f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 391f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 392f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 393452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Sets the state of the bottom sheet. The bottom sheet will transition to that state with 394452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * animation. 395452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 396f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @param state One of {@link #STATE_COLLAPSED}, {@link #STATE_EXPANDED}, or 397f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * {@link #STATE_HIDDEN}. 398452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 399452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final void setState(@State int state) { 400452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki V child = mViewRef.get(); 401452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (child == null) { 402452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return; 403452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 404452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int top; 405452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (state == STATE_COLLAPSED) { 406452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMaxOffset; 407452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else if (state == STATE_EXPANDED) { 408452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMinOffset; 409f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else if (mHideable && state == STATE_HIDDEN) { 410f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki top = mParentHeight; 411452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 412452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki throw new IllegalArgumentException("Illegal state argument: " + state); 413452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 414452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_SETTLING); 415452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) { 416452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(child, new SettleRunnable(child, state)); 417452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 418452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 419452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 420452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 421452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Gets the current state of the bottom sheet. 422452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 423452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @return One of {@link #STATE_EXPANDED}, {@link #STATE_COLLAPSED}, {@link #STATE_DRAGGING}, 424452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * and {@link #STATE_SETTLING}. 425452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 426452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 427452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final int getState() { 428452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return mState; 429452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 430452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 431452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private void setStateInternal(@State int state) { 432452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mState == state) { 433452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return; 434452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 435452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mState = state; 436dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki View bottomSheet = mViewRef.get(); 437dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki if (bottomSheet != null && mCallback != null) { 438dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki mCallback.onStateChanged(bottomSheet, state); 439f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 440f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 441f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 442e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private void reset() { 443e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mActivePointerId = ViewDragHelper.INVALID_POINTER; 444e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mVelocityTracker != null) { 445e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.recycle(); 446e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker = null; 447e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 448e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 449e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 450f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private boolean shouldHide(View child, float yvel) { 451f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (child.getTop() < mMaxOffset) { 452f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki // It should not hide, but collapse. 453f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return false; 454f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 455f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki final float newTop = child.getTop() + yvel * HIDE_FRICTION; 456f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return Math.abs(newTop - mMaxOffset) / (float) mPeekHeight > HIDE_THRESHOLD; 457f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 458f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 4599aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki private View findScrollingChild(View view) { 4609aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (view instanceof NestedScrollingChild) { 4619aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return view; 4629aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 4639aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (view instanceof ViewGroup) { 4649aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki ViewGroup group = (ViewGroup) view; 4659aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki for (int i = 0, count = group.getChildCount(); i < count; i++) { 4669aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki View scrollingChild = findScrollingChild(group.getChildAt(i)); 4679aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (scrollingChild != null) { 4689aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return scrollingChild; 4699aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 4709aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 4719aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 4729aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return null; 473452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 474452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 475e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private float getYVelocity() { 476e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 477e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId); 478e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 479e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 480452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() { 481452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 482452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 483452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean tryCaptureView(View child, int pointerId) { 484e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mState == STATE_DRAGGING) { 485e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return false; 486e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 487e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mState == STATE_EXPANDED && mActivePointerId == pointerId) { 4889aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki View scroll = mNestedScrollingChildRef.get(); 4899aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (scroll != null && ViewCompat.canScrollVertically(scroll, -1)) { 4909aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki // Let the content scroll up 4919aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return false; 4929aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 4939aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 494f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return mViewRef != null && mViewRef.get() == child; 495f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 496f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 497f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki @Override 498f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { 499f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki dispatchOnSlide(top); 500452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 501452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 502452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 503452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onViewDragStateChanged(int state) { 504452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (state == ViewDragHelper.STATE_DRAGGING) { 505452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_DRAGGING); 506452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 507452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 508452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 509452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 510452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onViewReleased(View releasedChild, float xvel, float yvel) { 511452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int top; 512452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State int targetState; 513f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (yvel < 0) { // Moving up 514452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMinOffset; 515452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki targetState = STATE_EXPANDED; 516f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else if (mHideable && shouldHide(releasedChild, yvel)) { 517f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki top = mParentHeight; 518f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki targetState = STATE_HIDDEN; 519452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 520452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMaxOffset; 521452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki targetState = STATE_COLLAPSED; 522452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 523452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) { 524f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki setStateInternal(STATE_SETTLING); 525452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(releasedChild, 526452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki new SettleRunnable(releasedChild, targetState)); 527f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else { 528f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki setStateInternal(targetState); 529452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 530452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 531452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 532452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 533452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public int clampViewPositionVertical(View child, int top, int dy) { 534f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return MathUtils.constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset); 535452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 536452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 537452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 538452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public int clampViewPositionHorizontal(View child, int left, int dx) { 539452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return child.getLeft(); 540452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 541f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 5429aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki @Override 5439aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki public int getViewVerticalDragRange(View child) { 5449aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (mHideable) { 5459aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return mParentHeight - mMinOffset; 5469aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } else { 5479aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return mMaxOffset - mMinOffset; 5489aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 5499aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 550452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki }; 551452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 552f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private void dispatchOnSlide(int top) { 553dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki View bottomSheet = mViewRef.get(); 554dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki if (bottomSheet != null && mCallback != null) { 555f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (top > mMaxOffset) { 556dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki mCallback.onSlide(bottomSheet, (float) (mMaxOffset - top) / mPeekHeight); 557f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else { 558dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki mCallback.onSlide(bottomSheet, 559dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki (float) (mMaxOffset - top) / ((mMaxOffset - mMinOffset))); 560f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 561f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 562f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 563f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 564452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private class SettleRunnable implements Runnable { 565452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 566452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final View mView; 567452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 568452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 569452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final int mTargetState; 570452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 571452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki SettleRunnable(View view, @State int targetState) { 572452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mView = view; 573452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mTargetState = targetState; 574452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 575452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 576452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 577452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void run() { 578452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) { 579452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(mView, this); 580452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 581452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(mTargetState); 582452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 583452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 584452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 585452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 586452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki protected static class SavedState extends View.BaseSavedState { 587452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 588452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 589452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki final int state; 590452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 591452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState(Parcel source) { 592452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super(source); 593452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki //noinspection ResourceType 594452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki state = source.readInt(); 595452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 596452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 597452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState(Parcelable superState, @State int state) { 598452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super(superState); 599452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki this.state = state; 600452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 601452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 602452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 603452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void writeToParcel(Parcel out, int flags) { 604452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super.writeToParcel(out, flags); 605452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki out.writeInt(state); 606452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 607452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 608452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final Parcelable.Creator<SavedState> CREATOR = 609452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki new Parcelable.Creator<SavedState>() { 610452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 611452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState createFromParcel(Parcel source) { 612452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return new SavedState(source); 613452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 614452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 615452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 616452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState[] newArray(int size) { 617452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return new SavedState[size]; 618452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 619452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki }; 620452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 621452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 622f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 623452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * A utility function to get the {@link BottomSheetBehavior} associated with the {@code view}. 624452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 625452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param view The {@link View} with {@link BottomSheetBehavior}. 626452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @return The {@link BottomSheetBehavior} associated with the {@code view}. 627452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 628452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @SuppressWarnings("unchecked") 629452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static <V extends View> BottomSheetBehavior<V> from(V view) { 630452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewGroup.LayoutParams params = view.getLayoutParams(); 631452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (!(params instanceof CoordinatorLayout.LayoutParams)) { 632452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki throw new IllegalArgumentException("The view is not a child of CoordinatorLayout"); 633452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 634452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params) 635452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki .getBehavior(); 636452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (!(behavior instanceof BottomSheetBehavior)) { 637452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki throw new IllegalArgumentException( 638452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki "The view is not associated with BottomSheetBehavior"); 639452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 640452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return (BottomSheetBehavior<V>) behavior; 641452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 642452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 643452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki} 644