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