BottomSheetBehavior.java revision d1e6ea34363dbb189490bb793692c86c2ebecf47
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 198e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 208e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas 21452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.content.Context; 22452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.content.res.TypedArray; 23452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.os.Parcel; 24452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.os.Parcelable; 25452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.annotation.IntDef; 26dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Arakiimport android.support.annotation.NonNull; 27c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo; 28a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Arakiimport android.support.annotation.VisibleForTesting; 29452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.design.R; 3005f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banesimport android.support.v4.os.ParcelableCompat; 3105f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banesimport android.support.v4.os.ParcelableCompatCreatorCallbacks; 3205f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banesimport android.support.v4.view.AbsSavedState; 33452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.view.MotionEventCompat; 34e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.support.v4.view.VelocityTrackerCompat; 35452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.view.ViewCompat; 36452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.support.v4.widget.ViewDragHelper; 37452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.util.AttributeSet; 3801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Arakiimport android.util.TypedValue; 39452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.MotionEvent; 40e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.view.VelocityTracker; 41452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.View; 42e3e7063749d095bba8743cad0aeb7164a567890dYuichi Arakiimport android.view.ViewConfiguration; 43452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport android.view.ViewGroup; 44bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Arakiimport android.view.ViewParent; 45452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 46452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.annotation.Retention; 47452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.annotation.RetentionPolicy; 48452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakiimport java.lang.ref.WeakReference; 49452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 50452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 51452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki/** 52452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * An interaction behavior plugin for a child view of {@link CoordinatorLayout} to make it work as 53452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * a bottom sheet. 54452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 55452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Arakipublic class BottomSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> { 56452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 57452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 584df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki * Callback for monitoring events about bottom sheets. 59f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 604df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki public abstract static class BottomSheetCallback { 61f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 62f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 63f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Called when the bottom sheet changes its state. 64f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 65dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * @param bottomSheet The bottom sheet view. 66dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * @param newState The new state. This will be one of {@link #STATE_DRAGGING}, 67dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * {@link #STATE_SETTLING}, {@link #STATE_EXPANDED}, 68dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * {@link #STATE_COLLAPSED}, or {@link #STATE_HIDDEN}. 69f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 70dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public abstract void onStateChanged(@NonNull View bottomSheet, @State int newState); 71f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 72f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 73f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Called when the bottom sheet is being dragged. 74f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 75dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki * @param bottomSheet The bottom sheet view. 7691177092bb7b2a0586f194de4d207349cdfabf30Aurimas Liutikas * @param slideOffset The new offset of this bottom sheet within [-1,1] range. Offset 7791177092bb7b2a0586f194de4d207349cdfabf30Aurimas Liutikas * increases as this bottom sheet is moving upward. From 0 to 1 the sheet 7891177092bb7b2a0586f194de4d207349cdfabf30Aurimas Liutikas * is between collapsed and expanded states and from -1 to 0 it is 7991177092bb7b2a0586f194de4d207349cdfabf30Aurimas Liutikas * between hidden and collapsed states. 80f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 81dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki public abstract void onSlide(@NonNull View bottomSheet, float slideOffset); 82f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 83f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 84f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 85452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is dragging. 86452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 87452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_DRAGGING = 1; 88452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 89452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 90452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is settling. 91452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 92452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_SETTLING = 2; 93452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 94452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 95452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is expanded. 96452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 97452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_EXPANDED = 3; 98452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 99452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 100452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * The bottom sheet is collapsed. 101452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 102452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static final int STATE_COLLAPSED = 4; 103452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 104f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 105f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * The bottom sheet is hidden. 106f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 107f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public static final int STATE_HIDDEN = 5; 108f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 109452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** @hide */ 1108e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 111f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki @IntDef({STATE_EXPANDED, STATE_COLLAPSED, STATE_DRAGGING, STATE_SETTLING, STATE_HIDDEN}) 112452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Retention(RetentionPolicy.SOURCE) 113452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public @interface State {} 114452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 11501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki /** 11601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki * Peek at the 16:9 ratio keyline of its parent. 117645afdf27340a6620c9970d14923b556e3c66574Yuichi Araki * 118645afdf27340a6620c9970d14923b556e3c66574Yuichi Araki * <p>This can be used as a parameter for {@link #setPeekHeight(int)}. 119645afdf27340a6620c9970d14923b556e3c66574Yuichi Araki * {@link #getPeekHeight()} will return this when the value is set.</p> 12001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki */ 12101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki public static final int PEEK_HEIGHT_AUTO = -1; 12201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki 123f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private static final float HIDE_THRESHOLD = 0.5f; 124f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 125f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki private static final float HIDE_FRICTION = 0.1f; 126f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 127e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private float mMaximumVelocity; 128f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 129452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private int mPeekHeight; 130452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 13101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki private boolean mPeekHeightAuto; 13201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki 13301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki private int mPeekHeightMin; 13401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki 135657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas int mMinOffset; 136452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 137657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas int mMaxOffset; 138452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 139657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas boolean mHideable; 140f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 141362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki private boolean mSkipCollapsed; 142362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki 143452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 144657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas int mState = STATE_COLLAPSED; 145452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 146657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas ViewDragHelper mViewDragHelper; 147452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 148452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private boolean mIgnoreEvents; 149452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 150e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private int mLastNestedScrollDy; 151e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 15249a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki private boolean mNestedScrolled; 15349a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki 154657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas int mParentHeight; 155452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 156657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas WeakReference<V> mViewRef; 157452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 158657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas WeakReference<View> mNestedScrollingChildRef; 159f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 1609aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki private BottomSheetCallback mCallback; 161f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 162e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private VelocityTracker mVelocityTracker; 163e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 164657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas int mActivePointerId; 165f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 1664eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki private int mInitialY; 1674eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki 168657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas boolean mTouchingScrollingChild; 169b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki 170452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 171452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Default constructor for instantiating BottomSheetBehaviors. 172452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 173452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public BottomSheetBehavior() { 174452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 175452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 176452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 177452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Default constructor for inflating BottomSheetBehaviors from layout. 178452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 179452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param context The {@link Context}. 180452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param attrs The {@link AttributeSet}. 181452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 182452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public BottomSheetBehavior(Context context, AttributeSet attrs) { 183452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super(context, attrs); 184452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki TypedArray a = context.obtainStyledAttributes(attrs, 185c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes R.styleable.BottomSheetBehavior_Layout); 18601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki TypedValue value = a.peekValue(R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight); 187606579592e9e9303a89cae2cf9bfe9f21572eec3Yuichi Araki if (value != null && value.data == PEEK_HEIGHT_AUTO) { 18801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki setPeekHeight(value.data); 18901ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } else { 19001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki setPeekHeight(a.getDimensionPixelSize( 19101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight, PEEK_HEIGHT_AUTO)); 19201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 193c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes setHideable(a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_hideable, false)); 194362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki setSkipCollapsed(a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_skipCollapsed, 195362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki false)); 196452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki a.recycle(); 197e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewConfiguration configuration = ViewConfiguration.get(context); 198e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 199452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 200452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 201452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 202452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) { 203452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return new SavedState(super.onSaveInstanceState(parent, child), mState); 204452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 205452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 206452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 207452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) { 208452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki SavedState ss = (SavedState) state; 209452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super.onRestoreInstanceState(parent, child, ss.getSuperState()); 210452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Intermediate states are restored as collapsed state 211f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (ss.state == STATE_DRAGGING || ss.state == STATE_SETTLING) { 212452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mState = STATE_COLLAPSED; 213f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else { 214f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki mState = ss.state; 215452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 216452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 217452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 218452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 219452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) { 2206bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki if (ViewCompat.getFitsSystemWindows(parent) && !ViewCompat.getFitsSystemWindows(child)) { 2216bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki ViewCompat.setFitsSystemWindows(child, true); 222f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 2236bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki int savedTop = child.getTop(); 2246bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki // First let the parent lay it out 2256bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki parent.onLayoutChild(child, layoutDirection); 226452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Offset the bottom sheet 227452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mParentHeight = parent.getHeight(); 22801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki int peekHeight; 22901ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (mPeekHeightAuto) { 23001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (mPeekHeightMin == 0) { 23101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mPeekHeightMin = parent.getResources().getDimensionPixelSize( 23201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki R.dimen.design_bottom_sheet_peek_height_min); 23301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 23401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki peekHeight = Math.max(mPeekHeightMin, mParentHeight - parent.getWidth() * 9 / 16); 23501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } else { 23601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki peekHeight = mPeekHeight; 23701ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 238452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mMinOffset = Math.max(0, mParentHeight - child.getHeight()); 23901ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mMaxOffset = Math.max(mParentHeight - peekHeight, mMinOffset); 240452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mState == STATE_EXPANDED) { 241452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.offsetTopAndBottom(child, mMinOffset); 242f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else if (mHideable && mState == STATE_HIDDEN) { 243f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki ViewCompat.offsetTopAndBottom(child, mParentHeight); 244f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else if (mState == STATE_COLLAPSED) { 245452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.offsetTopAndBottom(child, mMaxOffset); 2466bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki } else if (mState == STATE_DRAGGING || mState == STATE_SETTLING) { 2476bef5a31fd4bbfc70b21c0f694bcb272e88400a3Yuichi Araki ViewCompat.offsetTopAndBottom(child, savedTop - child.getTop()); 248452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 249452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper == null) { 250452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewDragHelper = ViewDragHelper.create(parent, mDragCallback); 251452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 252452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewRef = new WeakReference<>(child); 2539aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child)); 254452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return true; 255452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 256452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 257452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 258452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { 25917fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki if (!child.isShown()) { 2606cb44089e8c7a5abf8aa7a0cdff3cb6888790708Yuichi Araki mIgnoreEvents = true; 26117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki return false; 26217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 263452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int action = MotionEventCompat.getActionMasked(event); 264e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki // Record the velocity 265e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (action == MotionEvent.ACTION_DOWN) { 266e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki reset(); 267e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 268e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mVelocityTracker == null) { 269e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker = VelocityTracker.obtain(); 270e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 271e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.addMovement(event); 272452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki switch (action) { 273452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_UP: 274452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_CANCEL: 275b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki mTouchingScrollingChild = false; 276e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mActivePointerId = MotionEvent.INVALID_POINTER_ID; 277452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki // Reset the ignore flag 278452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mIgnoreEvents) { 279452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mIgnoreEvents = false; 280452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return false; 281452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 282452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki break; 283452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki case MotionEvent.ACTION_DOWN: 2844eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki int initialX = (int) event.getX(); 2854eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki mInitialY = (int) event.getY(); 286eb0922a6531ce1debff929814fff4b3df4159a45Geoffrey Métais View scroll = mNestedScrollingChildRef != null 287eb0922a6531ce1debff929814fff4b3df4159a45Geoffrey Métais ? mNestedScrollingChildRef.get() : null; 2884eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki if (scroll != null && parent.isPointInChildBounds(scroll, initialX, mInitialY)) { 289e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mActivePointerId = event.getPointerId(event.getActionIndex()); 290b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki mTouchingScrollingChild = true; 2919aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 292e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mIgnoreEvents = mActivePointerId == MotionEvent.INVALID_POINTER_ID && 2934eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki !parent.isPointInChildBounds(child, initialX, mInitialY); 294452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki break; 295452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 2964eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki if (!mIgnoreEvents && mViewDragHelper.shouldInterceptTouchEvent(event)) { 2974eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki return true; 2984eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki } 2994eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki // We have to handle cases that the ViewDragHelper does not capture the bottom sheet because 3004eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki // it is not the top most view of its parent. This is not necessary when the touch event is 3014eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki // happening over the scrolling content as nested scrolling logic handles that case. 3024eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki View scroll = mNestedScrollingChildRef.get(); 3034eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki return action == MotionEvent.ACTION_MOVE && scroll != null && 3044eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki !mIgnoreEvents && mState != STATE_DRAGGING && 3054eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki !parent.isPointInChildBounds(scroll, (int) event.getX(), (int) event.getY()) && 3064eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop(); 307452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 308452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 309452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 310452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { 31117fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki if (!child.isShown()) { 31217fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki return false; 31317fc77f5ca7c91daeab2e110669d9baf9d1a8090Yuichi Araki } 314e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int action = MotionEventCompat.getActionMasked(event); 315e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mState == STATE_DRAGGING && action == MotionEvent.ACTION_DOWN) { 316e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return true; 317e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 318452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mViewDragHelper.processTouchEvent(event); 319e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki // Record the velocity 320e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (action == MotionEvent.ACTION_DOWN) { 321e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki reset(); 322e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 323e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mVelocityTracker == null) { 324e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker = VelocityTracker.obtain(); 325e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 326e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.addMovement(event); 3274eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki // The ViewDragHelper tries to capture only the top-most View. We have to explicitly tell it 3284eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki // to capture the bottom sheet in case it is not captured and the touch slop is passed. 329912d40d353f5b6d0a4fa18740ca40aa4b25b0f9cYuichi Araki if (action == MotionEvent.ACTION_MOVE && !mIgnoreEvents) { 3304eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki if (Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop()) { 3314eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki mViewDragHelper.captureChildView(child, event.getPointerId(event.getActionIndex())); 3324eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki } 3334eb453c75e552a2e0231a93468d9597a4069286eYuichi Araki } 3341dbdc6baec537b45e9bd8e5afabcb5c35f1a8004Yuichi Araki return !mIgnoreEvents; 335452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 336452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 337e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 338e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, 339e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki View directTargetChild, View target, int nestedScrollAxes) { 340e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mLastNestedScrollDy = 0; 34149a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki mNestedScrolled = false; 342e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; 343e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 344e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 345e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 346e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, 347e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int dy, int[] consumed) { 348e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki View scrollingChild = mNestedScrollingChildRef.get(); 349e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (target != scrollingChild) { 350e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return; 351e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 352e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int currentTop = child.getTop(); 353e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int newTop = currentTop - dy; 354e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (dy > 0) { // Upward 355e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (newTop < mMinOffset) { 356e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = currentTop - mMinOffset; 357e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -consumed[1]); 358e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_EXPANDED); 359e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 360e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = dy; 361e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -dy); 362e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_DRAGGING); 363e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 364e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else if (dy < 0) { // Downward 365e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (!ViewCompat.canScrollVertically(target, -1)) { 366e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (newTop <= mMaxOffset || mHideable) { 367e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = dy; 368e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -dy); 369e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_DRAGGING); 370e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 371e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki consumed[1] = currentTop - mMaxOffset; 372e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.offsetTopAndBottom(child, -consumed[1]); 373e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_COLLAPSED); 374e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 375e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 376e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 377e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki dispatchOnSlide(child.getTop()); 378e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mLastNestedScrollDy = dy; 37949a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki mNestedScrolled = true; 380e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 381e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 382e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 383e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { 384b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki if (child.getTop() == mMinOffset) { 385b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki setStateInternal(STATE_EXPANDED); 386b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return; 387b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 388d1e6ea34363dbb189490bb793692c86c2ebecf47Geoffrey Métais if (mNestedScrollingChildRef == null || target != mNestedScrollingChildRef.get() 389d1e6ea34363dbb189490bb793692c86c2ebecf47Geoffrey Métais || !mNestedScrolled) { 390e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return; 391e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 392e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int top; 393e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki int targetState; 394e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mLastNestedScrollDy > 0) { 395e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki top = mMinOffset; 396e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki targetState = STATE_EXPANDED; 397e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else if (mHideable && shouldHide(child, getYVelocity())) { 398e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki top = mParentHeight; 399e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki targetState = STATE_HIDDEN; 40049a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } else if (mLastNestedScrollDy == 0) { 40149a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki int currentTop = child.getTop(); 40249a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) { 40349a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki top = mMinOffset; 40449a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki targetState = STATE_EXPANDED; 40549a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } else { 40649a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki top = mMaxOffset; 40749a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki targetState = STATE_COLLAPSED; 40849a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } 409e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 410e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki top = mMaxOffset; 411e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki targetState = STATE_COLLAPSED; 412e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 413e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) { 414e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(STATE_SETTLING); 415e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState)); 416e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } else { 417e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki setStateInternal(targetState); 418e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 41949a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki mNestedScrolled = false; 420e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 421e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 422e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki @Override 423e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, 424e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki float velocityX, float velocityY) { 425e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return target == mNestedScrollingChildRef.get() && 426e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki (mState != STATE_EXPANDED || 427e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki super.onNestedPreFling(coordinatorLayout, child, target, 428e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki velocityX, velocityY)); 429e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 430e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 431452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 432452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Sets the height of the bottom sheet when it is collapsed. 433452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 43401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki * @param peekHeight The height of the collapsed bottom sheet in pixels, or 435a36fbe5e02568bfae82904e7c7ac39438bde4551Yuichi Araki * {@link #PEEK_HEIGHT_AUTO} to configure the sheet to peek automatically 436a36fbe5e02568bfae82904e7c7ac39438bde4551Yuichi Araki * at 16:9 ratio keyline. 43773e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_peekHeight 438452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 439452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final void setPeekHeight(int peekHeight) { 44001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki boolean layout = false; 44101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (peekHeight == PEEK_HEIGHT_AUTO) { 44201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (!mPeekHeightAuto) { 44301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mPeekHeightAuto = true; 44401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki layout = true; 44501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 44601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } else if (mPeekHeightAuto || mPeekHeight != peekHeight) { 44701ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mPeekHeightAuto = false; 44801ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mPeekHeight = Math.max(0, peekHeight); 44901ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki mMaxOffset = mParentHeight - peekHeight; 45001ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki layout = true; 45101ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 45201ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (layout && mState == STATE_COLLAPSED && mViewRef != null) { 45301ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki V view = mViewRef.get(); 45401ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki if (view != null) { 45501ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki view.requestLayout(); 45601ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 45701ca48bfc8ad80dae2e2809420af1e4b80dd4ae5Yuichi Araki } 458452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 459452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 460452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 461452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Gets the height of the bottom sheet when it is collapsed. 462452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 463a36fbe5e02568bfae82904e7c7ac39438bde4551Yuichi Araki * @return The height of the collapsed bottom sheet in pixels, or {@link #PEEK_HEIGHT_AUTO} 464a36fbe5e02568bfae82904e7c7ac39438bde4551Yuichi Araki * if the sheet is configured to peek automatically at 16:9 ratio keyline 46573e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_peekHeight 466452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 467452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final int getPeekHeight() { 468a36fbe5e02568bfae82904e7c7ac39438bde4551Yuichi Araki return mPeekHeightAuto ? PEEK_HEIGHT_AUTO : mPeekHeight; 469452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 470452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 471452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 472f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Sets whether this bottom sheet can hide when it is swiped down. 473f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 474f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @param hideable {@code true} to make this bottom sheet hideable. 47573e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_hideable 476f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 477f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public void setHideable(boolean hideable) { 478f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki mHideable = hideable; 479f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 480f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 481f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 482f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * Gets whether this bottom sheet can hide when it is swiped down. 483f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 484f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @return {@code true} if this bottom sheet can hide. 48573e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_hideable 486f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 487f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public boolean isHideable() { 488f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return mHideable; 489f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 490f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 491f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 492362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * Sets whether this bottom sheet should skip the collapsed state when it is being hidden 493362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * after it is expanded once. Setting this to true has no effect unless the sheet is hideable. 494362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * 495362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * @param skipCollapsed True if the bottom sheet should skip the collapsed state. 496362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_skipCollapsed 497362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki */ 498362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki public void setSkipCollapsed(boolean skipCollapsed) { 499362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki mSkipCollapsed = skipCollapsed; 500362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 501362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki 502362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki /** 503362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * Sets whether this bottom sheet should skip the collapsed state when it is being hidden 504362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * after it is expanded once. 505362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * 506362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * @return Whether the bottom sheet should skip the collapsed state. 507362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_skipCollapsed 508362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki */ 509362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki public boolean getSkipCollapsed() { 510362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki return mSkipCollapsed; 511362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 512362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki 513362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki /** 5144df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki * Sets a callback to be notified of bottom sheet events. 515f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * 5164df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki * @param callback The callback to notify when bottom sheet events occur. 517f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki */ 5184df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki public void setBottomSheetCallback(BottomSheetCallback callback) { 5194df3c15e330c2f9730d3aa712f57befec8536dffYuichi Araki mCallback = callback; 520f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 521f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 522f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 523452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Sets the state of the bottom sheet. The bottom sheet will transition to that state with 524452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * animation. 525452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 526f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * @param state One of {@link #STATE_COLLAPSED}, {@link #STATE_EXPANDED}, or 527f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki * {@link #STATE_HIDDEN}. 528452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 529bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki public final void setState(final @State int state) { 530e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki if (state == mState) { 531e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki return; 532e0573dfb5896eb66e266ea366d8c58437c739c95Yuichi Araki } 53398475d29e771d7b15e4a109b22f410c7c1cf33d7Taeho Kim if (mViewRef == null) { 5346deb3a9dc52be7ab61702c430bb327343ef099acYuichi Araki // The view is not laid out yet; modify mState and let onLayoutChild handle it later 5356deb3a9dc52be7ab61702c430bb327343ef099acYuichi Araki if (state == STATE_COLLAPSED || state == STATE_EXPANDED || 5366deb3a9dc52be7ab61702c430bb327343ef099acYuichi Araki (mHideable && state == STATE_HIDDEN)) { 5376deb3a9dc52be7ab61702c430bb327343ef099acYuichi Araki mState = state; 5386deb3a9dc52be7ab61702c430bb327343ef099acYuichi Araki } 53998475d29e771d7b15e4a109b22f410c7c1cf33d7Taeho Kim return; 54098475d29e771d7b15e4a109b22f410c7c1cf33d7Taeho Kim } 541bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki final V child = mViewRef.get(); 542452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (child == null) { 543452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return; 544452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 545bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki // Start the animation; wait until a pending layout if there is one. 546bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki ViewParent parent = child.getParent(); 547bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki if (parent != null && parent.isLayoutRequested() && ViewCompat.isAttachedToWindow(child)) { 548bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki child.post(new Runnable() { 549bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki @Override 550bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki public void run() { 551bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki startSettlingAnimation(child, state); 552bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } 553bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki }); 554452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 555bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki startSettlingAnimation(child, state); 556452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 557452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 558452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 559452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki /** 560452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * Gets the current state of the bottom sheet. 561452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 562452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @return One of {@link #STATE_EXPANDED}, {@link #STATE_COLLAPSED}, {@link #STATE_DRAGGING}, 563452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * and {@link #STATE_SETTLING}. 564452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 565452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 566452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public final int getState() { 567452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return mState; 568452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 569452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 570657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas void setStateInternal(@State int state) { 571452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mState == state) { 572452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return; 573452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 574452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mState = state; 575dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki View bottomSheet = mViewRef.get(); 576dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki if (bottomSheet != null && mCallback != null) { 577dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki mCallback.onStateChanged(bottomSheet, state); 578f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 579f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 580f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 581e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private void reset() { 582e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mActivePointerId = ViewDragHelper.INVALID_POINTER; 583e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mVelocityTracker != null) { 584e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.recycle(); 585e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker = null; 586e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 587e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 588e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 589657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas boolean shouldHide(View child, float yvel) { 590362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki if (mSkipCollapsed) { 591362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki return true; 592362585b01e5ca19d1c58e4b152ad0a863b5f6d91Yuichi Araki } 593f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (child.getTop() < mMaxOffset) { 594f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki // It should not hide, but collapse. 595f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return false; 596f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 597f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki final float newTop = child.getTop() + yvel * HIDE_FRICTION; 598f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return Math.abs(newTop - mMaxOffset) / (float) mPeekHeight > HIDE_THRESHOLD; 599f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 600f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 60147a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire @VisibleForTesting 60247a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire View findScrollingChild(View view) { 60347a4a1aac79f0ad2279c7a3eeaad1673646f4d81Kassim Maguire if (ViewCompat.isNestedScrollingEnabled(view)) { 6049aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return view; 6059aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 6069aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (view instanceof ViewGroup) { 6079aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki ViewGroup group = (ViewGroup) view; 6089aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki for (int i = 0, count = group.getChildCount(); i < count; i++) { 6099aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki View scrollingChild = findScrollingChild(group.getChildAt(i)); 6109aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (scrollingChild != null) { 6119aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return scrollingChild; 6129aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 6139aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 6149aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 6159aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return null; 616452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 617452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 618e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki private float getYVelocity() { 619e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 620e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId); 621e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 622e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki 623657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas void startSettlingAnimation(View child, int state) { 624bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki int top; 625bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki if (state == STATE_COLLAPSED) { 626bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki top = mMaxOffset; 627bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } else if (state == STATE_EXPANDED) { 628bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki top = mMinOffset; 629bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } else if (mHideable && state == STATE_HIDDEN) { 630bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki top = mParentHeight; 631bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } else { 632bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki throw new IllegalArgumentException("Illegal state argument: " + state); 633bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } 634bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki setStateInternal(STATE_SETTLING); 635bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) { 636bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki ViewCompat.postOnAnimation(child, new SettleRunnable(child, state)); 637bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } 638bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki } 639bfd8da2625c8198bd7d6884acb9f4b73ff35a2afYuichi Araki 640452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() { 641452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 642452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 643452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public boolean tryCaptureView(View child, int pointerId) { 644e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mState == STATE_DRAGGING) { 645e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki return false; 646e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki } 647b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki if (mTouchingScrollingChild) { 648b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki return false; 649b1eb1357ec33343b7271a6d3876ac4af84e15510Yuichi Araki } 650e3e7063749d095bba8743cad0aeb7164a567890dYuichi Araki if (mState == STATE_EXPANDED && mActivePointerId == pointerId) { 6519aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki View scroll = mNestedScrollingChildRef.get(); 6529aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (scroll != null && ViewCompat.canScrollVertically(scroll, -1)) { 6539aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki // Let the content scroll up 6549aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return false; 6559aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 6569aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 657f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return mViewRef != null && mViewRef.get() == child; 658f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 659f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 660f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki @Override 661f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { 662f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki dispatchOnSlide(top); 663452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 664452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 665452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 666452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onViewDragStateChanged(int state) { 667452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (state == ViewDragHelper.STATE_DRAGGING) { 668452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(STATE_DRAGGING); 669452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 670452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 671452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 672452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 673452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void onViewReleased(View releasedChild, float xvel, float yvel) { 674452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki int top; 675452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State int targetState; 676f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (yvel < 0) { // Moving up 677452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMinOffset; 678452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki targetState = STATE_EXPANDED; 679f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else if (mHideable && shouldHide(releasedChild, yvel)) { 680f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki top = mParentHeight; 681f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki targetState = STATE_HIDDEN; 68249a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } else if (yvel == 0.f) { 68349a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki int currentTop = releasedChild.getTop(); 68449a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) { 68549a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki top = mMinOffset; 68649a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki targetState = STATE_EXPANDED; 68749a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } else { 68849a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki top = mMaxOffset; 68949a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki targetState = STATE_COLLAPSED; 69049a5904a773a7cfac127c5938671ed078d8ec8f3Yuichi Araki } 691452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 692452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki top = mMaxOffset; 693452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki targetState = STATE_COLLAPSED; 694452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 695452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) { 696f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki setStateInternal(STATE_SETTLING); 697452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(releasedChild, 698452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki new SettleRunnable(releasedChild, targetState)); 699f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else { 700f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki setStateInternal(targetState); 701452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 702452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 703452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 704452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 705452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public int clampViewPositionVertical(View child, int top, int dy) { 706f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki return MathUtils.constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset); 707452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 708452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 709452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 710452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public int clampViewPositionHorizontal(View child, int left, int dx) { 711452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return child.getLeft(); 712452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 713f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 7149aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki @Override 7159aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki public int getViewVerticalDragRange(View child) { 7169aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki if (mHideable) { 7179aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return mParentHeight - mMinOffset; 7189aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } else { 7199aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki return mMaxOffset - mMinOffset; 7209aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 7219aec720809a123c6193304730acf8b55d6ce5a7aYuichi Araki } 722452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki }; 723452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 724657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas void dispatchOnSlide(int top) { 725dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki View bottomSheet = mViewRef.get(); 726dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki if (bottomSheet != null && mCallback != null) { 727f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki if (top > mMaxOffset) { 728e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki mCallback.onSlide(bottomSheet, (float) (mMaxOffset - top) / 729e9864e3df037a1a5610752c6addc4d71e6b292c7Yuichi Araki (mParentHeight - mMaxOffset)); 730f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } else { 731dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki mCallback.onSlide(bottomSheet, 732dffd8d4be91b2e5e0ce66ad96867182db0c02fd0Yuichi Araki (float) (mMaxOffset - top) / ((mMaxOffset - mMinOffset))); 733f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 734f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 735f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki } 736f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki 737a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki @VisibleForTesting 738a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki int getPeekHeightMin() { 739a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki return mPeekHeightMin; 740a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki } 741a7a51521e1ecd8a009e4bd618520af05d9917671Yuichi Araki 742452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private class SettleRunnable implements Runnable { 743452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 744452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final View mView; 745452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 746452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 747452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki private final int mTargetState; 748452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 749452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki SettleRunnable(View view, @State int targetState) { 750452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mView = view; 751452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki mTargetState = targetState; 752452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 753452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 754452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 755452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void run() { 756452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) { 757452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewCompat.postOnAnimation(mView, this); 758452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } else { 759452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki setStateInternal(mTargetState); 760452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 761452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 762452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 763452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 76405f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes protected static class SavedState extends AbsSavedState { 765452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @State 766452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki final int state; 767452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 768452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState(Parcel source) { 76905f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes this(source, null); 77005f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes } 77105f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes 77205f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes public SavedState(Parcel source, ClassLoader loader) { 77305f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes super(source, loader); 774452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki //noinspection ResourceType 775452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki state = source.readInt(); 776452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 777452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 778452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState(Parcelable superState, @State int state) { 779452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super(superState); 780452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki this.state = state; 781452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 782452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 783452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 784452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public void writeToParcel(Parcel out, int flags) { 785452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki super.writeToParcel(out, flags); 786452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki out.writeInt(state); 787452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 788452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 78905f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator( 79005f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes new ParcelableCompatCreatorCallbacks<SavedState>() { 791452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 79205f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes public SavedState createFromParcel(Parcel in, ClassLoader loader) { 79305f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes return new SavedState(in, loader); 794452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 795452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 796452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @Override 797452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public SavedState[] newArray(int size) { 798452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return new SavedState[size]; 799452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 80005f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes }); 801452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 802452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 803f28a0f76c7a46d61785ef7cbc407c19942ecab46Yuichi Araki /** 804452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * A utility function to get the {@link BottomSheetBehavior} associated with the {@code view}. 805452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * 806452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @param view The {@link View} with {@link BottomSheetBehavior}. 807452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki * @return The {@link BottomSheetBehavior} associated with the {@code view}. 808452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki */ 809452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki @SuppressWarnings("unchecked") 810452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki public static <V extends View> BottomSheetBehavior<V> from(V view) { 811452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki ViewGroup.LayoutParams params = view.getLayoutParams(); 812452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (!(params instanceof CoordinatorLayout.LayoutParams)) { 813452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki throw new IllegalArgumentException("The view is not a child of CoordinatorLayout"); 814452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 815452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params) 816452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki .getBehavior(); 817452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki if (!(behavior instanceof BottomSheetBehavior)) { 818452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki throw new IllegalArgumentException( 819452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki "The view is not associated with BottomSheetBehavior"); 820452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 821452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki return (BottomSheetBehavior<V>) behavior; 822452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki } 823452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki 824452f94d1c58e2a7476019b98c3bf0e4b322d1525Yuichi Araki} 825