BottomSheetBehavior.java revision 01ca48bfc8ad80dae2e2809420af1e4b80dd4ae5
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; 2605f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banesimport android.support.v4.os.ParcelableCompat; 2705f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banesimport android.support.v4.os.ParcelableCompatCreatorCallbacks; 2805f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banesimport android.support.v4.view.AbsSavedState; 29452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.view.MotionEventCompat; 309aec720809a123c6193304730acf8b55d6ce5a7aYuichi Arakiimport android.support.v4.view.NestedScrollingChild; 31e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.support.v4.view.VelocityTrackerCompat; 32452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.view.ViewCompat; 33452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.widget.ViewDragHelper; 34452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.util.AttributeSet; 3501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Arakiimport android.util.TypedValue; 36452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.MotionEvent; 37e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.view.VelocityTracker; 38452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.View; 39e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.view.ViewConfiguration; 40452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.ViewGroup; 41452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 42452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.annotation.Retention; 43452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.annotation.RetentionPolicy; 44452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.ref.WeakReference; 45452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 46452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 47452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki/** 48452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * An interaction behavior plugin for a child view of {@link CoordinatorLayout} to make it work as 49452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * a bottom sheet. 50452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 51452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakipublic class BottomSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> { 52452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 53452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 544df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki * Callback for monitoring events about bottom sheets. 55f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 564df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki public abstract static class BottomSheetCallback { 57f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 58f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 59f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Called when the bottom sheet changes its state. 60f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 61dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * @param bottomSheet The bottom sheet view. 62dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * @param newState The new state. This will be one of {@link #STATE_DRAGGING}, 63dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * {@link #STATE_SETTLING}, {@link #STATE_EXPANDED}, 64dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * {@link #STATE_COLLAPSED}, or {@link #STATE_HIDDEN}. 65f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 66dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public abstract void onStateChanged(@NonNull View bottomSheet, @State int newState); 67f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 68f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 69f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Called when the bottom sheet is being dragged. 70f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 71dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * @param bottomSheet The bottom sheet view. 7291177092bb7b2a0586f194de4d207349cdfabf30Aurimas Liutikas * @param slideOffset The new offset of this bottom sheet within [-1,1] range. Offset 7391177092bb7b2a0586f194de4d207349cdfabf30Aurimas Liutikas * increases as this bottom sheet is moving upward. From 0 to 1 the sheet 7491177092bb7b2a0586f194de4d207349cdfabf30Aurimas Liutikas * is between collapsed and expanded states and from -1 to 0 it is 7591177092bb7b2a0586f194de4d207349cdfabf30Aurimas Liutikas * between hidden and collapsed states. 76f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 77dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public abstract void onSlide(@NonNull View bottomSheet, float slideOffset); 78f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 79f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 80f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 81452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is dragging. 82452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 83452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_DRAGGING = 1; 84452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 85452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 86452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is settling. 87452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 88452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_SETTLING = 2; 89452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 90452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 91452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is expanded. 92452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 93452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_EXPANDED = 3; 94452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 95452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 96452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is collapsed. 97452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 98452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_COLLAPSED = 4; 99452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 100f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 101f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * The bottom sheet is hidden. 102f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 103f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public static final int STATE_HIDDEN = 5; 104f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 105452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** @hide */ 106f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki @IntDef({STATE_EXPANDED, STATE_COLLAPSED, STATE_DRAGGING, STATE_SETTLING, STATE_HIDDEN}) 107452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Retention(RetentionPolicy.SOURCE) 108452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public @interface State {} 109452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 11001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki /** 11101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki * Peek at the 16:9 ratio keyline of its parent. 11201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki */ 11301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki public static final int PEEK_HEIGHT_AUTO = -1; 11401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki 115f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private static final float HIDE_THRESHOLD = 0.5f; 116f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 117f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private static final float HIDE_FRICTION = 0.1f; 118f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 119e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private float mMaximumVelocity; 120f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 121452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mPeekHeight; 122452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 12301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki private boolean mPeekHeightAuto; 12401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki 12501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki private int mPeekHeightMin; 12601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki 127452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mMinOffset; 128452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 129452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mMaxOffset; 130452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 131f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private boolean mHideable; 132f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 133362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki private boolean mSkipCollapsed; 134362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki 135452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 136452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mState = STATE_COLLAPSED; 137452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 138452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private ViewDragHelper mViewDragHelper; 139452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 140452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private boolean mIgnoreEvents; 141452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 142e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private int mLastNestedScrollDy; 143e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 14449a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki private boolean mNestedScrolled; 14549a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki 146452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mParentHeight; 147452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 148452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private WeakReference<V> mViewRef; 149452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 1509aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki private WeakReference<View> mNestedScrollingChildRef; 151f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 1529aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki private BottomSheetCallback mCallback; 153f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 154e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private VelocityTracker mVelocityTracker; 155e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 156e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private int mActivePointerId; 157f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 1584eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki private int mInitialY; 1594eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki 160b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki private boolean mTouchingScrollingChild; 161b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki 162452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 163452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Default constructor for instantiating BottomSheetBehaviors. 164452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 165452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public BottomSheetBehavior() { 166452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 167452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 168452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 169452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Default constructor for inflating BottomSheetBehaviors from layout. 170452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 171452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param context The {@link Context}. 172452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param attrs The {@link AttributeSet}. 173452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 174452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public BottomSheetBehavior(Context context, AttributeSet attrs) { 175452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super(context, attrs); 176452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki TypedArray a = context.obtainStyledAttributes(attrs, 177c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes R.styleable.BottomSheetBehavior_Layout); 17801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki TypedValue value = a.peekValue(R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight); 17901ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (value.data == PEEK_HEIGHT_AUTO) { 18001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki setPeekHeight(value.data); 18101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } else { 18201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki setPeekHeight(a.getDimensionPixelSize( 18301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight, PEEK_HEIGHT_AUTO)); 18401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 185c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes setHideable(a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_hideable, false)); 186362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki setSkipCollapsed(a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_skipCollapsed, 187362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki false)); 188452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki a.recycle(); 189e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewConfiguration configuration = ViewConfiguration.get(context); 190e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 191452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 192452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 193452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 194452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) { 195452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return new SavedState(super.onSaveInstanceState(parent, child), mState); 196452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 197452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 198452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 199452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) { 200452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki SavedState ss = (SavedState) state; 201452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super.onRestoreInstanceState(parent, child, ss.getSuperState()); 202452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Intermediate states are restored as collapsed state 203f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (ss.state == STATE_DRAGGING || ss.state == STATE_SETTLING) { 204452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mState = STATE_COLLAPSED; 205f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else { 206f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki mState = ss.state; 207452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 208452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 209452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 210452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 211452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) { 2126bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki if (ViewCompat.getFitsSystemWindows(parent) && !ViewCompat.getFitsSystemWindows(child)) { 2136bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki ViewCompat.setFitsSystemWindows(child, true); 214f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 2156bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki int savedTop = child.getTop(); 2166bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // First let the parent lay it out 2176bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki parent.onLayoutChild(child, layoutDirection); 218452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Offset the bottom sheet 219452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mParentHeight = parent.getHeight(); 22001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki int peekHeight; 22101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (mPeekHeightAuto) { 22201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (mPeekHeightMin == 0) { 22301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mPeekHeightMin = parent.getResources().getDimensionPixelSize( 22401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki R.dimen.design_bottom_sheet_peek_height_min); 22501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 22601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki peekHeight = Math.max(mPeekHeightMin, mParentHeight - parent.getWidth() * 9 / 16); 22701ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } else { 22801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki peekHeight = mPeekHeight; 22901ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 230452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mMinOffset = Math.max(0, mParentHeight - child.getHeight()); 23101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mMaxOffset = Math.max(mParentHeight - peekHeight, mMinOffset); 232452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mState == STATE_EXPANDED) { 233452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.offsetTopAndBottom(child, mMinOffset); 234f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else if (mHideable && mState == STATE_HIDDEN) { 235f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki ViewCompat.offsetTopAndBottom(child, mParentHeight); 236f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else if (mState == STATE_COLLAPSED) { 237452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.offsetTopAndBottom(child, mMaxOffset); 2386bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } else if (mState == STATE_DRAGGING || mState == STATE_SETTLING) { 2396bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki ViewCompat.offsetTopAndBottom(child, savedTop - child.getTop()); 240452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 241452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper == null) { 242452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewDragHelper = ViewDragHelper.create(parent, mDragCallback); 243452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 244452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewRef = new WeakReference<>(child); 2459aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child)); 246452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return true; 247452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 248452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 249452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 250452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { 25117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki if (!child.isShown()) { 25217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki return false; 25317fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 254452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int action = MotionEventCompat.getActionMasked(event); 255e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki // Record the velocity 256e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (action == MotionEvent.ACTION_DOWN) { 257e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki reset(); 258e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 259e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mVelocityTracker == null) { 260e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker = VelocityTracker.obtain(); 261e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 262e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.addMovement(event); 263452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki switch (action) { 264452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_UP: 265452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_CANCEL: 266b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki mTouchingScrollingChild = false; 267e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mActivePointerId = MotionEvent.INVALID_POINTER_ID; 268452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Reset the ignore flag 269452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mIgnoreEvents) { 270452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mIgnoreEvents = false; 271452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return false; 272452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 273452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki break; 274452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_DOWN: 2754eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki int initialX = (int) event.getX(); 2764eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki mInitialY = (int) event.getY(); 2779aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki View scroll = mNestedScrollingChildRef.get(); 2784eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki if (scroll != null && parent.isPointInChildBounds(scroll, initialX, mInitialY)) { 279e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mActivePointerId = event.getPointerId(event.getActionIndex()); 280b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki mTouchingScrollingChild = true; 2819aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 282e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mIgnoreEvents = mActivePointerId == MotionEvent.INVALID_POINTER_ID && 2834eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki !parent.isPointInChildBounds(child, initialX, mInitialY); 284452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki break; 285452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 2864eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki if (!mIgnoreEvents && mViewDragHelper.shouldInterceptTouchEvent(event)) { 2874eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki return true; 2884eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki } 2894eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki // We have to handle cases that the ViewDragHelper does not capture the bottom sheet because 2904eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki // it is not the top most view of its parent. This is not necessary when the touch event is 2914eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki // happening over the scrolling content as nested scrolling logic handles that case. 2924eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki View scroll = mNestedScrollingChildRef.get(); 2934eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki return action == MotionEvent.ACTION_MOVE && scroll != null && 2944eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki !mIgnoreEvents && mState != STATE_DRAGGING && 2954eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki !parent.isPointInChildBounds(scroll, (int) event.getX(), (int) event.getY()) && 2964eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop(); 297452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 298452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 299452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 300452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { 30117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki if (!child.isShown()) { 30217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki return false; 30317fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 304e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int action = MotionEventCompat.getActionMasked(event); 305e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mState == STATE_DRAGGING && action == MotionEvent.ACTION_DOWN) { 306e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return true; 307e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 308452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewDragHelper.processTouchEvent(event); 309e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki // Record the velocity 310e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (action == MotionEvent.ACTION_DOWN) { 311e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki reset(); 312e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 313e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mVelocityTracker == null) { 314e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker = VelocityTracker.obtain(); 315e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 316e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.addMovement(event); 3174eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki // The ViewDragHelper tries to capture only the top-most View. We have to explicitly tell it 3184eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki // to capture the bottom sheet in case it is not captured and the touch slop is passed. 319912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki if (action == MotionEvent.ACTION_MOVE && !mIgnoreEvents) { 3204eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki if (Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop()) { 3214eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki mViewDragHelper.captureChildView(child, event.getPointerId(event.getActionIndex())); 3224eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki } 3234eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki } 3241dbdc6baec537b45e9bd8e5afabcb5c35f1a8004Yuichi Araki return !mIgnoreEvents; 325452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 326452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 327e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 328e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, 329e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki View directTargetChild, View target, int nestedScrollAxes) { 330e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mLastNestedScrollDy = 0; 33149a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki mNestedScrolled = false; 332e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; 333e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 334e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 335e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 336e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, 337e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int dy, int[] consumed) { 338e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki View scrollingChild = mNestedScrollingChildRef.get(); 339e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (target != scrollingChild) { 340e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return; 341e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 342e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int currentTop = child.getTop(); 343e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int newTop = currentTop - dy; 344e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (dy > 0) { // Upward 345e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (newTop < mMinOffset) { 346e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = currentTop - mMinOffset; 347e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -consumed[1]); 348e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_EXPANDED); 349e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 350e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = dy; 351e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -dy); 352e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_DRAGGING); 353e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 354e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else if (dy < 0) { // Downward 355e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (!ViewCompat.canScrollVertically(target, -1)) { 356e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (newTop <= mMaxOffset || mHideable) { 357e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = dy; 358e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -dy); 359e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_DRAGGING); 360e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 361e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = currentTop - mMaxOffset; 362e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -consumed[1]); 363e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_COLLAPSED); 364e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 365e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 366e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 367e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki dispatchOnSlide(child.getTop()); 368e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mLastNestedScrollDy = dy; 36949a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki mNestedScrolled = true; 370e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 371e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 372e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 373e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { 374b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki if (child.getTop() == mMinOffset) { 375b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki setStateInternal(STATE_EXPANDED); 376b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return; 377b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 378b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki if (target != mNestedScrollingChildRef.get() || !mNestedScrolled) { 379e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return; 380e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 381e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int top; 382e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int targetState; 383e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mLastNestedScrollDy > 0) { 384e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki top = mMinOffset; 385e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki targetState = STATE_EXPANDED; 386e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else if (mHideable && shouldHide(child, getYVelocity())) { 387e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki top = mParentHeight; 388e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki targetState = STATE_HIDDEN; 38949a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } else if (mLastNestedScrollDy == 0) { 39049a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki int currentTop = child.getTop(); 39149a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) { 39249a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki top = mMinOffset; 39349a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki targetState = STATE_EXPANDED; 39449a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } else { 39549a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki top = mMaxOffset; 39649a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki targetState = STATE_COLLAPSED; 39749a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } 398e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 399e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki top = mMaxOffset; 400e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki targetState = STATE_COLLAPSED; 401e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 402e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) { 403e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_SETTLING); 404e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState)); 405e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 406e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(targetState); 407e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 40849a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki mNestedScrolled = false; 409e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 410e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 411e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 412e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, 413e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki float velocityX, float velocityY) { 414e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return target == mNestedScrollingChildRef.get() && 415e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki (mState != STATE_EXPANDED || 416e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki super.onNestedPreFling(coordinatorLayout, child, target, 417e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki velocityX, velocityY)); 418e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 419e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 420452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 421452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Sets the height of the bottom sheet when it is collapsed. 422452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 42301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki * @param peekHeight The height of the collapsed bottom sheet in pixels, or 42401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki * {@link #PEEK_HEIGHT_AUTO}. 42573e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_peekHeight 426452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 427452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final void setPeekHeight(int peekHeight) { 42801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki boolean layout = false; 42901ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (peekHeight == PEEK_HEIGHT_AUTO) { 43001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (!mPeekHeightAuto) { 43101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mPeekHeightAuto = true; 43201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki layout = true; 43301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 43401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } else if (mPeekHeightAuto || mPeekHeight != peekHeight) { 43501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mPeekHeightAuto = false; 43601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mPeekHeight = Math.max(0, peekHeight); 43701ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mMaxOffset = mParentHeight - peekHeight; 43801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki layout = true; 43901ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 44001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (layout && mState == STATE_COLLAPSED && mViewRef != null) { 44101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki V view = mViewRef.get(); 44201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (view != null) { 44301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki view.requestLayout(); 44401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 44501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 446452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 447452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 448452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 449452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Gets the height of the bottom sheet when it is collapsed. 450452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 451452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @return The height of the collapsed bottom sheet. 45273e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_peekHeight 453452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 454452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final int getPeekHeight() { 455452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return mPeekHeight; 456452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 457452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 458452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 459f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Sets whether this bottom sheet can hide when it is swiped down. 460f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 461f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @param hideable {@code true} to make this bottom sheet hideable. 46273e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_hideable 463f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 464f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public void setHideable(boolean hideable) { 465f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki mHideable = hideable; 466f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 467f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 468f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 469f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Gets whether this bottom sheet can hide when it is swiped down. 470f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 471f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @return {@code true} if this bottom sheet can hide. 47273e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_hideable 473f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 474f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public boolean isHideable() { 475f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return mHideable; 476f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 477f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 478f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 479362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * Sets whether this bottom sheet should skip the collapsed state when it is being hidden 480362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * after it is expanded once. Setting this to true has no effect unless the sheet is hideable. 481362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * 482362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * @param skipCollapsed True if the bottom sheet should skip the collapsed state. 483362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_skipCollapsed 484362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki */ 485362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki public void setSkipCollapsed(boolean skipCollapsed) { 486362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki mSkipCollapsed = skipCollapsed; 487362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 488362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki 489362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki /** 490362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * Sets whether this bottom sheet should skip the collapsed state when it is being hidden 491362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * after it is expanded once. 492362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * 493362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * @return Whether the bottom sheet should skip the collapsed state. 494362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_skipCollapsed 495362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki */ 496362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki public boolean getSkipCollapsed() { 497362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki return mSkipCollapsed; 498362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 499362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki 500362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki /** 5014df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki * Sets a callback to be notified of bottom sheet events. 502f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 5034df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki * @param callback The callback to notify when bottom sheet events occur. 504f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 5054df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki public void setBottomSheetCallback(BottomSheetCallback callback) { 5064df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki mCallback = callback; 507f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 508f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 509f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 510452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Sets the state of the bottom sheet. The bottom sheet will transition to that state with 511452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * animation. 512452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 513f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @param state One of {@link #STATE_COLLAPSED}, {@link #STATE_EXPANDED}, or 514f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * {@link #STATE_HIDDEN}. 515452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 516452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final void setState(@State int state) { 517e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki if (state == mState) { 518e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki return; 519e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki } 52098475d29e771d7b15e4a109b22f410c7c1cf33d7Taeho Kim if (mViewRef == null) { 5216deb3a9dc52be7ab61702c430bb327343ef099acYuichi Araki // The view is not laid out yet; modify mState and let onLayoutChild handle it later 5226deb3a9dc52be7ab61702c430bb327343ef099acYuichi Araki if (state == STATE_COLLAPSED || state == STATE_EXPANDED || 5236deb3a9dc52be7ab61702c430bb327343ef099acYuichi Araki (mHideable && state == STATE_HIDDEN)) { 5246deb3a9dc52be7ab61702c430bb327343ef099acYuichi Araki mState = state; 5256deb3a9dc52be7ab61702c430bb327343ef099acYuichi Araki } 52698475d29e771d7b15e4a109b22f410c7c1cf33d7Taeho Kim return; 52798475d29e771d7b15e4a109b22f410c7c1cf33d7Taeho Kim } 528452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki V child = mViewRef.get(); 529452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (child == null) { 530452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return; 531452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 532452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int top; 533452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (state == STATE_COLLAPSED) { 534452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMaxOffset; 535452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else if (state == STATE_EXPANDED) { 536452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMinOffset; 537f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else if (mHideable && state == STATE_HIDDEN) { 538f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki top = mParentHeight; 539452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 540452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki throw new IllegalArgumentException("Illegal state argument: " + state); 541452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 542452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_SETTLING); 543452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) { 544452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(child, new SettleRunnable(child, state)); 545452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 546452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 547452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 548452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 549452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Gets the current state of the bottom sheet. 550452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 551452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @return One of {@link #STATE_EXPANDED}, {@link #STATE_COLLAPSED}, {@link #STATE_DRAGGING}, 552452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * and {@link #STATE_SETTLING}. 553452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 554452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 555452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final int getState() { 556452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return mState; 557452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 558452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 559452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private void setStateInternal(@State int state) { 560452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mState == state) { 561452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return; 562452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 563452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mState = state; 564dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki View bottomSheet = mViewRef.get(); 565dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki if (bottomSheet != null && mCallback != null) { 566dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki mCallback.onStateChanged(bottomSheet, state); 567f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 568f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 569f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 570e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private void reset() { 571e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mActivePointerId = ViewDragHelper.INVALID_POINTER; 572e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mVelocityTracker != null) { 573e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.recycle(); 574e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker = null; 575e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 576e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 577e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 578f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private boolean shouldHide(View child, float yvel) { 579362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki if (mSkipCollapsed) { 580362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki return true; 581362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 582f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (child.getTop() < mMaxOffset) { 583f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki // It should not hide, but collapse. 584f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return false; 585f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 586f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki final float newTop = child.getTop() + yvel * HIDE_FRICTION; 587f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return Math.abs(newTop - mMaxOffset) / (float) mPeekHeight > HIDE_THRESHOLD; 588f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 589f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 5909aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki private View findScrollingChild(View view) { 5919aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (view instanceof NestedScrollingChild) { 5929aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return view; 5939aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 5949aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (view instanceof ViewGroup) { 5959aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki ViewGroup group = (ViewGroup) view; 5969aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki for (int i = 0, count = group.getChildCount(); i < count; i++) { 5979aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki View scrollingChild = findScrollingChild(group.getChildAt(i)); 5989aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (scrollingChild != null) { 5999aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return scrollingChild; 6009aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 6019aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 6029aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 6039aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return null; 604452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 605452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 606e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private float getYVelocity() { 607e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 608e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId); 609e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 610e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 611452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() { 612452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 613452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 614452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean tryCaptureView(View child, int pointerId) { 615e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mState == STATE_DRAGGING) { 616e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return false; 617e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 618b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki if (mTouchingScrollingChild) { 619b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return false; 620b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 621e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mState == STATE_EXPANDED && mActivePointerId == pointerId) { 6229aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki View scroll = mNestedScrollingChildRef.get(); 6239aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (scroll != null && ViewCompat.canScrollVertically(scroll, -1)) { 6249aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki // Let the content scroll up 6259aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return false; 6269aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 6279aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 628f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return mViewRef != null && mViewRef.get() == child; 629f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 630f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 631f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki @Override 632f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { 633f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki dispatchOnSlide(top); 634452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 635452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 636452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 637452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onViewDragStateChanged(int state) { 638452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (state == ViewDragHelper.STATE_DRAGGING) { 639452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_DRAGGING); 640452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 641452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 642452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 643452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 644452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onViewReleased(View releasedChild, float xvel, float yvel) { 645452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int top; 646452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State int targetState; 647f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (yvel < 0) { // Moving up 648452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMinOffset; 649452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki targetState = STATE_EXPANDED; 650f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else if (mHideable && shouldHide(releasedChild, yvel)) { 651f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki top = mParentHeight; 652f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki targetState = STATE_HIDDEN; 65349a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } else if (yvel == 0.f) { 65449a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki int currentTop = releasedChild.getTop(); 65549a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) { 65649a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki top = mMinOffset; 65749a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki targetState = STATE_EXPANDED; 65849a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } else { 65949a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki top = mMaxOffset; 66049a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki targetState = STATE_COLLAPSED; 66149a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } 662452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 663452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMaxOffset; 664452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki targetState = STATE_COLLAPSED; 665452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 666452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) { 667f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki setStateInternal(STATE_SETTLING); 668452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(releasedChild, 669452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki new SettleRunnable(releasedChild, targetState)); 670f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else { 671f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki setStateInternal(targetState); 672452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 673452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 674452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 675452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 676452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public int clampViewPositionVertical(View child, int top, int dy) { 677f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return MathUtils.constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset); 678452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 679452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 680452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 681452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public int clampViewPositionHorizontal(View child, int left, int dx) { 682452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return child.getLeft(); 683452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 684f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 6859aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki @Override 6869aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki public int getViewVerticalDragRange(View child) { 6879aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (mHideable) { 6889aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return mParentHeight - mMinOffset; 6899aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } else { 6909aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return mMaxOffset - mMinOffset; 6919aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 6929aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 693452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki }; 694452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 695f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private void dispatchOnSlide(int top) { 696dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki View bottomSheet = mViewRef.get(); 697dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki if (bottomSheet != null && mCallback != null) { 698f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (top > mMaxOffset) { 699dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki mCallback.onSlide(bottomSheet, (float) (mMaxOffset - top) / mPeekHeight); 700f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else { 701dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki mCallback.onSlide(bottomSheet, 702dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki (float) (mMaxOffset - top) / ((mMaxOffset - mMinOffset))); 703f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 704f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 705f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 706f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 707452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private class SettleRunnable implements Runnable { 708452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 709452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final View mView; 710452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 711452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 712452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final int mTargetState; 713452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 714452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki SettleRunnable(View view, @State int targetState) { 715452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mView = view; 716452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mTargetState = targetState; 717452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 718452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 719452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 720452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void run() { 721452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) { 722452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(mView, this); 723452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 724452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(mTargetState); 725452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 726452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 727452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 728452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 72905f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes protected static class SavedState extends AbsSavedState { 730452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 731452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki final int state; 732452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 733452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState(Parcel source) { 73405f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes this(source, null); 73505f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes } 73605f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes 73705f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes public SavedState(Parcel source, ClassLoader loader) { 73805f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes super(source, loader); 739452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki //noinspection ResourceType 740452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki state = source.readInt(); 741452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 742452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 743452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState(Parcelable superState, @State int state) { 744452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super(superState); 745452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki this.state = state; 746452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 747452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 748452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 749452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void writeToParcel(Parcel out, int flags) { 750452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super.writeToParcel(out, flags); 751452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki out.writeInt(state); 752452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 753452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 75405f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator( 75505f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes new ParcelableCompatCreatorCallbacks<SavedState>() { 756452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 75705f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes public SavedState createFromParcel(Parcel in, ClassLoader loader) { 75805f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes return new SavedState(in, loader); 759452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 760452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 761452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 762452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState[] newArray(int size) { 763452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return new SavedState[size]; 764452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 76505f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes }); 766452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 767452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 768f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 769452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * A utility function to get the {@link BottomSheetBehavior} associated with the {@code view}. 770452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 771452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param view The {@link View} with {@link BottomSheetBehavior}. 772452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @return The {@link BottomSheetBehavior} associated with the {@code view}. 773452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 774452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @SuppressWarnings("unchecked") 775452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static <V extends View> BottomSheetBehavior<V> from(V view) { 776452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewGroup.LayoutParams params = view.getLayoutParams(); 777452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (!(params instanceof CoordinatorLayout.LayoutParams)) { 778452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki throw new IllegalArgumentException("The view is not a child of CoordinatorLayout"); 779452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 780452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params) 781452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki .getBehavior(); 782452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (!(behavior instanceof BottomSheetBehavior)) { 783452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki throw new IllegalArgumentException( 784452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki "The view is not associated with BottomSheetBehavior"); 785452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 786452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return (BottomSheetBehavior<V>) behavior; 787452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 788452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 789452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki} 790