AppBarLayout.java revision 8818e8e12682e5df1535cd9b2d792494bd3e059d
1a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/*
2a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Copyright (C) 2015 The Android Open Source Project
3a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
4a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * you may not use this file except in compliance with the License.
6a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * You may obtain a copy of the License at
7a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
8a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
10a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Unless required by applicable law or agreed to in writing, software
11a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * See the License for the specific language governing permissions and
14a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * limitations under the License.
15a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */
16a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
17a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespackage android.support.design.widget;
18a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
19a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.Context;
20a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.res.TypedArray;
21e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcel;
22e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcelable;
2350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport android.support.annotation.IntDef;
2472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.annotation.NonNull;
2572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.annotation.Nullable;
26a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.design.R;
2762ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banesimport android.support.v4.os.ParcelableCompat;
2862ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banesimport android.support.v4.os.ParcelableCompatCreatorCallbacks;
29a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat;
306ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banesimport android.support.v4.view.WindowInsetsCompat;
31a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet;
32a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View;
33a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup;
34a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator;
35a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout;
36a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
3750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.Retention;
3850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.RetentionPolicy;
39d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banesimport java.lang.ref.WeakReference;
40631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.ArrayList;
41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List;
42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/**
44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of
459fb154338a62edc2c57dc036895199d6f1769400Chris Banes * material designs app bar concept, namely scrolling gestures.
46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through
48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute:
49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}.
50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}.
53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will
54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work.
55a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
569fb154338a62edc2c57dc036895199d6f1769400Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to know when to scroll.
579fb154338a62edc2c57dc036895199d6f1769400Chris Banes * The binding is done through the {@link ScrollingViewBehavior} behavior class, meaning that you
589fb154338a62edc2c57dc036895199d6f1769400Chris Banes * should set your scrolling view's behavior to be an instance of {@link ScrollingViewBehavior}.
599fb154338a62edc2c57dc036895199d6f1769400Chris Banes * A string resource containing the full class name is available.
60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre>
62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;android.support.design.widget.CoordinatorLayout
63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:android=&quot;http://schemas.android.com/apk/res/android";
64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:app=&quot;http://schemas.android.com/apk/res-auto";
65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_width=&quot;match_parent&quot;
66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_height=&quot;match_parent&quot;&gt;
67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.v4.widget.NestedScrollView
69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;
70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;match_parent&quot;
71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             app:layout_behavior=&quot;@string/appbar_scrolling_view_behavior&quot;&gt;
72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;!-- Your scrolling content --&gt;
74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.v4.widget.NestedScrollView&gt;
76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.design.widget.AppBarLayout
78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;wrap_content&quot;
79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;&gt;
80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.v7.widget.Toolbar
82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.design.widget.TabLayout
86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.design.widget.AppBarLayout&gt;
90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;/android.support.design.widget.CoordinatorLayout&gt;
92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre>
93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar">
95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a>
96a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */
97a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout {
99a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1009995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    private static final int PENDING_ACTION_NONE = 0x0;
1019995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    private static final int PENDING_ACTION_EXPANDED = 0x1;
1029995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    private static final int PENDING_ACTION_COLLAPSED = 0x2;
1039995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    private static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4;
1049995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
105a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
106631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical
107631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * offset changes.
108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
109631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public interface OnOffsetChangedListener {
110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows
112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * child views to implement custom behavior based on the offset (for instance pinning a
113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * view at a certain y value).
114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
115631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes         * @param appBarLayout the {@link AppBarLayout} which offset has changed
11650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes         * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px
117a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
118631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset);
119a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private static final int INVALID_SCROLL_RANGE = -1;
122a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mTotalScrollRange = INVALID_SCROLL_RANGE;
124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownPreScrollRange = INVALID_SCROLL_RANGE;
125a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownScrollRange = INVALID_SCROLL_RANGE;
126a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
127a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    boolean mHaveChildWithInterpolator;
128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
12950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    private float mTargetElevation;
13050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
1319995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    private int mPendingAction = PENDING_ACTION_NONE;
1329995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
1336ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    private WindowInsetsCompat mLastInsets;
1346ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes
1356f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes    private final List<OnOffsetChangedListener> mListeners;
136631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
137a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context) {
138a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        this(context, null);
139a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
140a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
141a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context, AttributeSet attrs) {
142a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super(context, attrs);
143a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        setOrientation(VERTICAL);
14450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
145809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes        ThemeUtils.checkAppCompatTheme(context);
146809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes
14750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout,
14850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                0, R.style.Widget_Design_AppBarLayout);
14950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0);
15081520564f3dd783136e025174021ba4eabd6ff3cChris Banes        setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background));
1519995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        if (a.hasValue(R.styleable.AppBarLayout_expanded)) {
1529995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false));
1539995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        }
15450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        a.recycle();
15550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
15650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        // Use the bounds view outline provider so that we cast a shadow, even without a background
15750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        ViewUtils.setBoundsViewOutlineProvider(this);
158631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
159631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        mListeners = new ArrayList<>();
160631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
161631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        ViewCompat.setElevation(this, mTargetElevation);
1626ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes
1636ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        ViewCompat.setOnApplyWindowInsetsListener(this,
1646ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                new android.support.v4.view.OnApplyWindowInsetsListener() {
1656ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    @Override
1666ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    public WindowInsetsCompat onApplyWindowInsets(View v,
1676ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                            WindowInsetsCompat insets) {
1688818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes                        return onWindowInsetChanged(insets);
1696ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    }
1706ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                });
171631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
172631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
173631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
174631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Add a listener that will be called when the offset of this {@link AppBarLayout} changes.
175631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
176631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param listener The listener that will be called when the offset changes.]
177631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
178631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @see #removeOnOffsetChangedListener(OnOffsetChangedListener)
179631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
180631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void addOnOffsetChangedListener(OnOffsetChangedListener listener) {
1816f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes        if (listener != null && !mListeners.contains(listener)) {
1826f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes            mListeners.add(listener);
183631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
184631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
185631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
186631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
187631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Remove the previously added {@link OnOffsetChangedListener}.
188631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
189631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param listener the listener to remove.
190631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
191631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) {
1926f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes        if (listener != null) {
1936f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes            mListeners.remove(listener);
194631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
195a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
198ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
199ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
200ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        invalidateScrollRanges();
201ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    }
202ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
203ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    @Override
204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected void onLayout(boolean changed, int l, int t, int r, int b) {
205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.onLayout(changed, l, t, r, b);
20679aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes        invalidateScrollRanges();
207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mHaveChildWithInterpolator = false;
209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams childLp = (LayoutParams) child.getLayoutParams();
212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final Interpolator interpolator = childLp.getScrollInterpolator();
213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (interpolator != null) {
215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mHaveChildWithInterpolator = true;
216a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
217a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
221ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private void invalidateScrollRanges() {
222ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        // Invalidate the scroll ranges
223ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        mTotalScrollRange = INVALID_SCROLL_RANGE;
224ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        mDownPreScrollRange = INVALID_SCROLL_RANGE;
225ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        mDownScrollRange = INVALID_SCROLL_RANGE;
226ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    }
227ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public void setOrientation(int orientation) {
230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (orientation != VERTICAL) {
231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            throw new IllegalArgumentException("AppBarLayout is always vertical and does"
232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    + " not support horizontal orientation");
233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
234a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.setOrientation(orientation);
235a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
2379995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    /**
2389995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * Sets whether this {@link AppBarLayout} is expanded or not, animating if it has already
2399995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * been laid out.
2409995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2419995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a
2429995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * direct child of a {@link CoordinatorLayout}.</p>
2439995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2449995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @param expanded true if the layout should be fully expanded, false if it should
2459995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *                 be fully collapsed
2469995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2479995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @attr ref android.support.design.R.styleable#AppBarLayout_expanded
2489995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     */
2499995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    public void setExpanded(boolean expanded) {
2509995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        setExpanded(expanded, ViewCompat.isLaidOut(this));
2519995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
2529995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
2539995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    /**
2549995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * Sets whether this {@link AppBarLayout} is expanded or not.
2559995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2569995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a
2579995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * direct child of a {@link CoordinatorLayout}.</p>
2589995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2599995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @param expanded true if the layout should be fully expanded, false if it should
2609995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *                 be fully collapsed
2619995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @param animate Whether to animate to the new state
2629995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2639995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @attr ref android.support.design.R.styleable#AppBarLayout_expanded
2649995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     */
2659995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    public void setExpanded(boolean expanded, boolean animate) {
2669995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        mPendingAction = (expanded ? PENDING_ACTION_EXPANDED : PENDING_ACTION_COLLAPSED)
2679995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                | (animate ? PENDING_ACTION_ANIMATE_ENABLED : 0);
2689995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        requestLayout();
2699995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
2709995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return p instanceof LayoutParams;
274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateDefaultLayoutParams() {
278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public LayoutParams generateLayoutParams(AttributeSet attrs) {
283a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(getContext(), attrs);
284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
286a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (p instanceof LinearLayout.LayoutParams) {
289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((LinearLayout.LayoutParams) p);
290a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        } else if (p instanceof MarginLayoutParams) {
291a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((MarginLayoutParams) p);
292a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
293a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(p);
294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
296ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private boolean hasChildWithInterpolator() {
297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mHaveChildWithInterpolator;
298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
3016ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes     * Returns the scroll range of all children.
302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     *
303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * @return the scroll range in px
304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
3056ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    public final int getTotalScrollRange() {
306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mTotalScrollRange != INVALID_SCROLL_RANGE) {
307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mTotalScrollRange;
308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
314ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            final int childHeight = child.getMeasuredHeight();
315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
319b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                range += childHeight + lp.topMargin + lp.bottomMargin;
320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing scroll, we to take the collapsed height into account.
3236ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    // We also break straight away since later views can't scroll beneath
324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // us
3256ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    range -= ViewCompat.getMinimumHeight(child);
3266ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    break;
327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
3344832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes        return mTotalScrollRange = Math.max(0, range - getTopInset());
335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
337ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private boolean hasScrollableChildren() {
338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return getTotalScrollRange() != 0;
339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling up from a nested pre-scroll.
343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
344ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private int getUpNestedPreScrollRange() {
34550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        return getTotalScrollRange();
346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested pre-scroll.
350a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
351ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private int getDownNestedPreScrollRange() {
352a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownPreScrollRange != INVALID_SCROLL_RANGE) {
353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownPreScrollRange;
355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = getChildCount() - 1; i >= 0; i--) {
359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
361ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            final int childHeight = child.getMeasuredHeight();
362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) {
365b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                // First take the margin into account
366b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                range += lp.topMargin + lp.bottomMargin;
367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // The view has the quick return flag combination...
368a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) {
369a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If they're set to enter collapsed, use the minimum height
370a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    range += ViewCompat.getMinimumHeight(child);
37147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                } else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
37247543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    // Only enter by the amount of the collapsed height
37347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    range += childHeight - ViewCompat.getMinimumHeight(child);
374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Else use the full height
376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    range += childHeight;
377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
378a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else if (range > 0) {
379a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // If we've hit an non-quick return scrollable view, and we've already hit a
380a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // quick return view, return now
381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
38447543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        return mDownPreScrollRange = Math.max(0, range - getTopInset());
385a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
386a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
387a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
388a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested scroll.
389a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
390ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private int getDownNestedScrollRange() {
391a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownScrollRange != INVALID_SCROLL_RANGE) {
392a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
393a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownScrollRange;
394a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
397c1ce4f6cf4d99d6856c4259bf1bb1cb56f604737Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
398a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
399a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
400ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            int childHeight = child.getMeasuredHeight();
401b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes            childHeight += lp.topMargin + lp.bottomMargin;
402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
404a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
405a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
406a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
407a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                range += childHeight;
408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing exit scroll, we to take the collapsed height into account.
4114832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes                    // We also break the range straight away since later views can't scroll
412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // beneath us
4134832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes                    range -= ViewCompat.getMinimumHeight(child) + getTopInset();
4144832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes                    break;
415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
417a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
420a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
421a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
4224c33be829067714342b629d29329206bc2116afeChris Banes        return mDownScrollRange = Math.max(0, range);
423a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
424a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
42550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    final int getMinimumHeightForVisibleOverlappingContent() {
42617ed3263761329f6aa6796941358c41001fff325Chris Banes        final int topInset = getTopInset();
42750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        final int minHeight = ViewCompat.getMinimumHeight(this);
42850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        if (minHeight != 0) {
42950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            // If this layout has a min height, use it (doubled)
4306ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes            return (minHeight * 2) + topInset;
43150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
43250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
43350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        // Otherwise, we'll use twice the min height of our last child
43450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        final int childCount = getChildCount();
43550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        return childCount >= 1
4366ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                ? (ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2) + topInset
43750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                : 0;
43850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    }
43950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
44050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    /**
441631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Set the elevation value to use when this {@link AppBarLayout} should be elevated
442631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * above content.
443631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * <p>
444631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * This method does not do anything itself. A typical use for this method is called from within
445631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * an {@link OnOffsetChangedListener} when the offset has changed in such a way to require an
446631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * elevation change.
447631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
448631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param elevation the elevation value to use.
449631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
450631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @see ViewCompat#setElevation(View, float)
45150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes     */
452631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void setTargetElevation(float elevation) {
453631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        mTargetElevation = elevation;
454631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
455631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
456631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
457631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Returns the elevation value to use when this {@link AppBarLayout} should be elevated
458631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * above content.
459631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
460631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public float getTargetElevation() {
46150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        return mTargetElevation;
46250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    }
46350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
464ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private int getPendingAction() {
4659995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        return mPendingAction;
4669995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
4679995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
468ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private void resetPendingAction() {
4699995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        mPendingAction = PENDING_ACTION_NONE;
4709995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
4719995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
472ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private int getTopInset() {
473ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        return mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
474ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    }
475ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
4768818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes    private WindowInsetsCompat onWindowInsetChanged(final WindowInsetsCompat insets) {
4778818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        WindowInsetsCompat newInsets = null;
4788818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes
4798818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        if (ViewCompat.getFitsSystemWindows(this)) {
4808818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            // If we're set to fit system windows, keep the insets
4818818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            newInsets = insets;
4828818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        }
4838818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes
4848818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        // If our insets have changed, keep them and invalidate the scroll ranges...
4858818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        if (newInsets != mLastInsets) {
4868818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            mLastInsets = newInsets;
4878818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            invalidateScrollRanges();
4888818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        }
4898818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes
49017ed3263761329f6aa6796941358c41001fff325Chris Banes        return insets;
4916ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    }
4926ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes
493a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class LayoutParams extends LinearLayout.LayoutParams {
49450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
49550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        /** @hide */
49650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @IntDef(flag=true, value={
49750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_SCROLL,
49850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_EXIT_UNTIL_COLLAPSED,
49950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_ENTER_ALWAYS,
500bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED,
501bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                SCROLL_FLAG_SNAP
50250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        })
50350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Retention(RetentionPolicy.SOURCE)
50450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public @interface ScrollFlags {}
50550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
506a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
507a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * The view will be scroll in direct relation to scroll events. This flag needs to be
508a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * set for any of the other flags to take effect. If any sibling views
509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * before this one do not have this flag, then this value has no effect.
510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
511a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_SCROLL = 0x1;
512a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
513a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
514a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When exiting (scrolling off screen) the view will be scrolled until it is
515a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * 'collapsed'. The collapsed height is defined by the view's minimum height.
516a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
517a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
519a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
520a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2;
521a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
522a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
523a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When entering (scrolling on screen) the view will scroll on any downwards
524a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * scroll event, regardless of whether the scrolling view is also scrolling. This
525a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * is commonly referred to as the 'quick return' pattern.
526a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
527a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4;
528a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
529a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
530a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * An additional flag for 'enterAlways' which modifies the returning view to
531a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * only initially scroll back to it's collapsed height. Once the scrolling view has
532a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * reached the end of it's scroll range, the remainder of this view will be scrolled
533a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * into view. The collapsed height is defined by the view's minimum height.
534a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
535a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
536a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
537a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
538a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8;
539a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
540a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
541bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * Upon a scroll ending, if the view is only partially visible then it will be snapped
542bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * and scrolled to it's closest edge. For example, if the view only has it's bottom 25%
543bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * displayed, it will be scrolled off screen completely. Conversely, if it's bottom 75%
544bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * is visible then it will be scrolled fully into view.
545bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         */
546bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        public static final int SCROLL_FLAG_SNAP = 0x10;
547bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
548bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        /**
549bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * Internal flags which allows quick checking features
550a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
551a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS;
552bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        static final int FLAG_SNAP = SCROLL_FLAG_SCROLL | SCROLL_FLAG_SNAP;
553a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
554a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int mScrollFlags = SCROLL_FLAG_SCROLL;
555a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        Interpolator mScrollInterpolator;
556a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
557a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(Context c, AttributeSet attrs) {
558a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(c, attrs);
559a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams);
560a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0);
561a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) {
562a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int resId = a.getResourceId(
563a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0);
564a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator(
565a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        c, resId);
566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
567a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
569a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
570a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height) {
571a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height);
572a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
573a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
574a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height, float weight) {
575a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height, weight);
576a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
577a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
578a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(ViewGroup.LayoutParams p) {
579a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(p);
580a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
581a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
582a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(MarginLayoutParams source) {
583a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
584a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
585a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
586a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LinearLayout.LayoutParams source) {
587a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
588a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
589a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
590a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LayoutParams source) {
591a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
592a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = source.mScrollFlags;
593a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = source.mScrollInterpolator;
594a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
596a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the scrolling flags.
598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL},
600bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         *             {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS},
601bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         *             {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED} and {@link #SCROLL_FLAG_SNAP }.
602a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
603a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollFlags()
604a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
605149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags
606a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
60750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public void setScrollFlags(@ScrollFlags int flags) {
608a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = flags;
609a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
610a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
611a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the scrolling flags.
613a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
614a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollFlags(int)
615a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
616149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags
617a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
61850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @ScrollFlags
619a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public int getScrollFlags() {
620a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollFlags;
621a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
622a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
623a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
624a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the interpolator to when scrolling the view associated with this
625a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}.
626a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
627a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling.
628a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
629149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator
630a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollInterpolator()
631a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
632a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void setScrollInterpolator(Interpolator interpolator) {
633a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = interpolator;
634a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
635a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
636a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
637a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the {@link Interpolator} being used for scrolling the view associated with this
638a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling.
639a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
640149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator
641a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollInterpolator(Interpolator)
642a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
643a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Interpolator getScrollInterpolator() {
644a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollInterpolator;
645a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
646a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
647a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
648a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
649a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested
650a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * scroll handling with offsetting.
651a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
652ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    public static class Behavior extends HeaderBehavior<AppBarLayout> {
653dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes        private static final int ANIMATE_OFFSET_DIPS_PER_SECOND = 300;
654e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private static final int INVALID_POSITION = -1;
655e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
65672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        /**
65772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         * Callback to allow control over any {@link AppBarLayout} dragging.
65872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         */
65972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        public static abstract class DragCallback {
66072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            /**
66172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * Allows control over whether the given {@link AppBarLayout} can be dragged or not.
66272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             *
66372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * <p>Dragging is defined as a direct touch on the AppBarLayout with movement. This
66472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * call does not affect any nested scrolling.</p>
66572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             *
66672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * @return true if we are in a position to scroll the AppBarLayout via a drag, false
66772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             *         if not.
66872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             */
66972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            public abstract boolean canDrag(@NonNull AppBarLayout appBarLayout);
67072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
67172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
672e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private int mOffsetDelta;
673a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
67450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        private boolean mSkipNestedPreScroll;
67547543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        private boolean mWasNestedFlung;
676631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
677631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private ValueAnimatorCompat mAnimator;
67850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
679e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private int mOffsetToChildIndexOnLayout = INVALID_POSITION;
680e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private boolean mOffsetToChildIndexOnLayoutIsMinHeight;
681e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private float mOffsetToChildIndexOnLayoutPerc;
682e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
683d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        private WeakReference<View> mLastNestedScrollingChildRef;
68472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        private DragCallback mOnDragCallback;
685d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
686a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior() {}
687a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
688a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior(Context context, AttributeSet attrs) {
689a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
690a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
691a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
692a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
693ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes        public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child,
694a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View directTargetChild, View target, int nestedScrollAxes) {
695f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes            // Return true if we're nested scrolling vertically, and we have scrollable children
696f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes            // and the scrolling view is big enough to scroll
697631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0
698f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes                    && child.hasScrollableChildren()
699ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes                    && parent.getHeight() - directTargetChild.getHeight() <= child.getHeight();
700631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
701631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (started && mAnimator != null) {
702631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // Cancel any offset animation
703631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.cancel();
704631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
705631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
706d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            // A new nested scroll has started so clear out the previous ref
707d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            mLastNestedScrollingChildRef = null;
708d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
709631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            return started;
710a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
711a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
712a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
713a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
714a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View target, int dx, int dy, int[] consumed) {
71550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            if (dy != 0 && !mSkipNestedPreScroll) {
71650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int min, max;
71750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                if (dy < 0) {
71850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // We're scrolling down
71950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    min = -child.getTotalScrollRange();
72050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    max = min + child.getDownNestedPreScrollRange();
72150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                } else {
72250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // We're scrolling up
72350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    min = -child.getUpNestedPreScrollRange();
72450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    max = 0;
72550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                }
72650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
727a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
728a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
729a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
730a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
731a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
732a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View target, int dxConsumed, int dyConsumed,
733a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int dxUnconsumed, int dyUnconsumed) {
73450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            if (dyUnconsumed < 0) {
73550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // If the scrolling view is scrolling down but not consuming, it's probably be at
73650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // the top of it's content
73750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                scroll(coordinatorLayout, child, dyUnconsumed,
73850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        -child.getDownNestedScrollRange(), 0);
73950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // Set the expanding flag so that onNestedPreScroll doesn't handle any events
74050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                mSkipNestedPreScroll = true;
74150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            } else {
74250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // As we're no longer handling nested scrolls, reset the skip flag
74350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                mSkipNestedPreScroll = false;
744a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
74550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
746a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
74750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Override
748bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl,
74950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                View target) {
75047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            if (!mWasNestedFlung) {
751bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                // If we haven't been flung then let's see if the current view has been set to snap
752bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                snapToChildIfNeeded(coordinatorLayout, abl);
753bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            }
754bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
755bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            // Reset the flags
75650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            mSkipNestedPreScroll = false;
75747543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            mWasNestedFlung = false;
758d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            // Keep a reference to the previous nested scrolling child
759d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            mLastNestedScrollingChildRef = new WeakReference<>(target);
760d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        }
761d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
762d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        @Override
763631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        public boolean onNestedFling(final CoordinatorLayout coordinatorLayout,
764631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                final AppBarLayout child, View target, float velocityX, float velocityY,
765631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                boolean consumed) {
766bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            boolean flung = false;
767bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
768631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (!consumed) {
769631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // It has been consumed so let's fling ourselves
770bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                flung = fling(coordinatorLayout, child, -child.getTotalScrollRange(),
771bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        0, -velocityY);
772631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            } else {
773631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // If we're scrolling up and the child also consumed the fling. We'll fake scroll
774631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // upto our 'collapsed' offset
775631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                if (velocityY < 0) {
776631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // We're scrolling down
777bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    final int targetScroll = -child.getTotalScrollRange()
778631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                            + child.getDownNestedPreScrollRange();
779bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    if (getTopBottomOffsetForScrollingSibling() < targetScroll) {
780bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        // If we're currently not expanded more than the target scroll, we'll
781bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        // animate a fling
782bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        animateOffsetTo(coordinatorLayout, child, targetScroll);
783bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        flung = true;
784631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
785631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                } else {
786631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // We're scrolling up
787bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    final int targetScroll = -child.getUpNestedPreScrollRange();
788bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    if (getTopBottomOffsetForScrollingSibling() > targetScroll) {
789bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        // If we're currently not expanded less than the target scroll, we'll
790bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        // animate a fling
791bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        animateOffsetTo(coordinatorLayout, child, targetScroll);
792bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        flung = true;
793631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
794631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                }
795631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
796631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
79747543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            mWasNestedFlung = flung;
798bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            return flung;
799631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
800631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
80172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        /**
80272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         * Set a callback to control any {@link AppBarLayout} dragging.
80372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         *
80472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         * @param callback the callback to use, or {@code null} to use the default behavior.
80572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         */
80672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        public void setDragCallback(@Nullable DragCallback callback) {
80772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            mOnDragCallback = callback;
80872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
80972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
810631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private void animateOffsetTo(final CoordinatorLayout coordinatorLayout,
811dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                final AppBarLayout child, final int offset) {
812dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            final int currentOffset = getTopBottomOffsetForScrollingSibling();
813dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            if (currentOffset == offset) {
814dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                if (mAnimator != null && mAnimator.isRunning()) {
815dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    mAnimator.cancel();
816dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                }
817dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                return;
818dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            }
819dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes
820631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (mAnimator == null) {
821631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator = ViewUtils.createAnimator();
822631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
823631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
824631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    @Override
825631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    public void onAnimationUpdate(ValueAnimatorCompat animator) {
826ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                        setHeaderTopBottomOffset(coordinatorLayout, child,
827631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                                animator.getAnimatedIntValue());
828631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
829631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                });
830631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            } else {
831631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.cancel();
832631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
833631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
834dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            // Set the duration based on the amount of dips we're travelling in
835dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            final float distanceDp = Math.abs(currentOffset - offset) /
836dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    coordinatorLayout.getResources().getDisplayMetrics().density;
837dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            mAnimator.setDuration(Math.round(distanceDp * 1000 / ANIMATE_OFFSET_DIPS_PER_SECOND));
838dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes
839dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            mAnimator.setIntValues(currentOffset, offset);
840631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            mAnimator.start();
841631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
842631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
843bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        private View getChildOnOffset(AppBarLayout abl, final int offset) {
844bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            for (int i = 0, count = abl.getChildCount(); i < count; i++) {
845bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                View child = abl.getChildAt(i);
846bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                if (child.getTop() <= -offset && child.getBottom() >= -offset) {
847bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    return child;
848bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                }
849bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            }
850bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            return null;
851bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        }
852bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
853bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        private void snapToChildIfNeeded(CoordinatorLayout coordinatorLayout, AppBarLayout abl) {
854bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            final int offset = getTopBottomOffsetForScrollingSibling();
855bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            final View offsetChild = getChildOnOffset(abl, offset);
856bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            if (offsetChild != null) {
857bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                final LayoutParams lp = (LayoutParams) offsetChild.getLayoutParams();
858bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                if ((lp.getScrollFlags() & LayoutParams.FLAG_SNAP) == LayoutParams.FLAG_SNAP) {
859bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    // We're set the snap, so animate the offset to the nearest edge
860dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    int childTop = -offsetChild.getTop();
861dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    int childBottom = -offsetChild.getBottom();
862dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes
863dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    // If the view is set only exit until it is collapsed, we'll abide by that
864dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    if ((lp.getScrollFlags() & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED)
865dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                            == LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) {
866dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                        childBottom += ViewCompat.getMinimumHeight(offsetChild);
867dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    }
868dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes
869dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    final int newOffset = offset < (childBottom + childTop) / 2
870dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                            ? childBottom : childTop;
871bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    animateOffsetTo(coordinatorLayout, abl,
872dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                            MathUtils.constrain(newOffset, -abl.getTotalScrollRange(), 0));
873bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                }
874bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            }
875bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        }
876bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
877631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        @Override
8789995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl,
87950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int layoutDirection) {
8809995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            boolean handled = super.onLayoutChild(parent, abl, layoutDirection);
8819995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
8829995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            final int pendingAction = abl.getPendingAction();
8839995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            if (pendingAction != PENDING_ACTION_NONE) {
8849995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                final boolean animate = (pendingAction & PENDING_ACTION_ANIMATE_ENABLED) != 0;
8859995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                if ((pendingAction & PENDING_ACTION_COLLAPSED) != 0) {
8869995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    final int offset = -abl.getUpNestedPreScrollRange();
8879995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    if (animate) {
8889995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                        animateOffsetTo(parent, abl, offset);
8899995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    } else {
890ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                        setHeaderTopBottomOffset(parent, abl, offset);
8919995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    }
8929995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                } else if ((pendingAction & PENDING_ACTION_EXPANDED) != 0) {
8939995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    if (animate) {
8949995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                        animateOffsetTo(parent, abl, 0);
8959995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    } else {
896ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                        setHeaderTopBottomOffset(parent, abl, 0);
8979995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    }
8989995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                }
8999995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            } else if (mOffsetToChildIndexOnLayout >= 0) {
9009995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                View child = abl.getChildAt(mOffsetToChildIndexOnLayout);
901e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                int offset = -child.getBottom();
902e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                if (mOffsetToChildIndexOnLayoutIsMinHeight) {
903e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    offset += ViewCompat.getMinimumHeight(child);
904e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                } else {
905e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc);
906e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                }
907e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                setTopAndBottomOffset(offset);
908e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
90950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
91079aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes            // Finally reset any pending states
91179aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes            abl.resetPendingAction();
91279aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes            mOffsetToChildIndexOnLayout = INVALID_POSITION;
91379aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes
91447543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            // We may have changed size, so let's constrain the top and bottom offset correctly,
91547543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            // just in case we're out of the bounds
91647543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            setTopAndBottomOffset(
91747543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    MathUtils.constrain(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0));
91847543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes
91950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            // Make sure we update the elevation
9209995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            dispatchOffsetUpdates(abl);
92150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
92250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            return handled;
923a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
924a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
92572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        @Override
92672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        boolean canDragView(AppBarLayout view) {
92772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            if (mOnDragCallback != null) {
92872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                // If there is a drag callback set, it's in control
92972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                return mOnDragCallback.canDrag(view);
93072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            }
93172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
93272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            // Else we'll use the default behaviour of seeing if it can scroll down
933d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            if (mLastNestedScrollingChildRef != null) {
93472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                // If we have a reference to a scrolling view, check it
93572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final View scrollingView = mLastNestedScrollingChildRef.get();
93672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                return scrollingView != null && scrollingView.isShown()
93772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                        && !ViewCompat.canScrollVertically(scrollingView, -1);
93872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            } else {
93972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                // Otherwise we assume that the scrolling view hasn't been scrolled and can drag.
94072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                return true;
941d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            }
942d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        }
943d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
944ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
94547543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        void onFlingFinished(CoordinatorLayout parent, AppBarLayout layout) {
94647543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            // At the end of a manual fling, check to see if we need to snap to the edge-child
94747543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            snapToChildIfNeeded(parent, layout);
94847543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        }
94947543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes
95047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        @Override
95172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getMaxDragOffset(AppBarLayout view) {
95272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            return -view.getDownNestedScrollRange();
95372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
95472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
95572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        @Override
95672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getScrollRangeForDragFling(AppBarLayout view) {
95772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            return view.getTotalScrollRange();
95872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
95972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
96072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        @Override
96172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int setHeaderTopBottomOffset(CoordinatorLayout coordinatorLayout,
96272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                AppBarLayout header, int newOffset, int minOffset, int maxOffset) {
963e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            final int curOffset = getTopBottomOffsetForScrollingSibling();
964a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            int consumed = 0;
965a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
9665e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes            if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
967631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // If we have some scrolling range, and we're currently within the min and max
968631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // offsets, calculate a new offset
96950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset);
970ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                AppBarLayout appBarLayout = (AppBarLayout) header;
971a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (curOffset != newOffset) {
972e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    final int interpolatedOffset = appBarLayout.hasChildWithInterpolator()
973e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                            ? interpolateOffset(appBarLayout, newOffset)
974e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                            : newOffset;
975e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
976e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset);
977631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
978a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update how much dy we have consumed
979a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    consumed = curOffset - newOffset;
980a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update the stored sibling offset
981e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    mOffsetDelta = newOffset - interpolatedOffset;
982a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
983a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) {
984a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // If the offset hasn't changed and we're using an interpolated scroll
985a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // then we need to keep any dependent views updated. CoL will do this for
986a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // us when we move, but we need to do it manually when we don't (as an
987a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // interpolated scroll may finish early).
988a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        coordinatorLayout.dispatchDependentViewsChanged(appBarLayout);
989a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
990a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
991631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // Dispatch the updates to any listeners
992631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    dispatchOffsetUpdates(appBarLayout);
993a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
9945e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes            } else {
9955e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                // Reset the offset delta
9965e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                mOffsetDelta = 0;
997a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
998a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
999a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return consumed;
1000a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1001a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1002631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private void dispatchOffsetUpdates(AppBarLayout layout) {
10036f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes            final List<OnOffsetChangedListener> listeners = layout.mListeners;
100450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
1005631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            // Iterate backwards through the list so that most recently added listeners
1006631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            // get the first chance to decide
1007631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            for (int i = 0, z = listeners.size(); i < z; i++) {
10086f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes                final OnOffsetChangedListener listener = listeners.get(i);
1009631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                if (listener != null) {
1010631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    listener.onOffsetChanged(layout, getTopAndBottomOffset());
1011a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
1012a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1013a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1014a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1015a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int interpolateOffset(AppBarLayout layout, final int offset) {
1016a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int absOffset = Math.abs(offset);
1017a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1018a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = layout.getChildCount(); i < z; i++) {
1019a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final View child = layout.getChildAt(i);
1020a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
102150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                final Interpolator interpolator = childLp.getScrollInterpolator();
1022a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
102350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                if (absOffset >= child.getTop() && absOffset <= child.getBottom()) {
1024a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (interpolator != null) {
102550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        int childScrollableHeight = 0;
102650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        final int flags = childLp.getScrollFlags();
102750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
1028b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                            // We're set to scroll so add the child's height plus margin
1029b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                            childScrollableHeight += child.getHeight() + childLp.topMargin
1030b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                                    + childLp.bottomMargin;
1031b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes
103250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
1033b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                                // For a collapsing scroll, we to take the collapsed height
1034b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                                // into account.
103550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                childScrollableHeight -= ViewCompat.getMinimumHeight(child);
103650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            }
103750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        }
103850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
1039ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                        if (ViewCompat.getFitsSystemWindows(child)) {
1040ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                            childScrollableHeight -= layout.getTopInset();
1041ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                        }
1042ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
104350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        if (childScrollableHeight > 0) {
104450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            final int offsetForView = absOffset - child.getTop();
104550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            final int interpolatedDiff = Math.round(childScrollableHeight *
104650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                    interpolator.getInterpolation(
104750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                            offsetForView / (float) childScrollableHeight));
104850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
104950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            return Integer.signum(offset) * (child.getTop() + interpolatedDiff);
105050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        }
1051a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
105250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
105350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // If we get to here then the view on the offset isn't suitable for interpolated
105450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // scrolling. So break out of the loop
105550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    break;
1056a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
1057a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1058a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1059a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return offset;
1060a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1061a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1062ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
106372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getTopBottomOffsetForScrollingSibling() {
1064e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            return getTopAndBottomOffset() + mOffsetDelta;
1065e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        }
1066e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1067e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        @Override
1068e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout) {
1069e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            final Parcelable superState = super.onSaveInstanceState(parent, appBarLayout);
1070e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            final int offset = getTopAndBottomOffset();
1071e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1072e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            // Try and find the first visible child...
1073e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            for (int i = 0, count = appBarLayout.getChildCount(); i < count; i++) {
1074e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                View child = appBarLayout.getChildAt(i);
1075e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                final int visBottom = child.getBottom() + offset;
1076e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1077e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                if (child.getTop() + offset <= 0 && visBottom >= 0) {
1078e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    final SavedState ss = new SavedState(superState);
1079e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    ss.firstVisibleChildIndex = i;
1080e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    ss.firstVisibileChildAtMinimumHeight =
1081e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                            visBottom == ViewCompat.getMinimumHeight(child);
1082e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    ss.firstVisibileChildPercentageShown = visBottom / (float) child.getHeight();
1083e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    return ss;
1084e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                }
1085e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1086e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1087e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            // Else we'll just return the super state
1088e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            return superState;
1089e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        }
1090e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1091e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        @Override
1092e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout,
1093e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                Parcelable state) {
1094e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            if (state instanceof SavedState) {
1095e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                final SavedState ss = (SavedState) state;
1096e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super.onRestoreInstanceState(parent, appBarLayout, ss.getSuperState());
1097e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                mOffsetToChildIndexOnLayout = ss.firstVisibleChildIndex;
1098e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                mOffsetToChildIndexOnLayoutPerc = ss.firstVisibileChildPercentageShown;
1099e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                mOffsetToChildIndexOnLayoutIsMinHeight = ss.firstVisibileChildAtMinimumHeight;
1100e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            } else {
1101e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super.onRestoreInstanceState(parent, appBarLayout, state);
1102e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                mOffsetToChildIndexOnLayout = INVALID_POSITION;
1103e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1104e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        }
1105e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
110662ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes        protected static class SavedState extends BaseSavedState {
1107e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            int firstVisibleChildIndex;
1108e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            float firstVisibileChildPercentageShown;
1109e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            boolean firstVisibileChildAtMinimumHeight;
1110e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
111162ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes            public SavedState(Parcel source, ClassLoader loader) {
1112e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super(source);
1113e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                firstVisibleChildIndex = source.readInt();
1114e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                firstVisibileChildPercentageShown = source.readFloat();
1115e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                firstVisibileChildAtMinimumHeight = source.readByte() != 0;
1116e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1117e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1118e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            public SavedState(Parcelable superState) {
1119e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super(superState);
1120e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1121e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1122e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            @Override
1123e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            public void writeToParcel(Parcel dest, int flags) {
1124e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super.writeToParcel(dest, flags);
1125e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                dest.writeInt(firstVisibleChildIndex);
1126e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                dest.writeFloat(firstVisibileChildPercentageShown);
1127e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                dest.writeByte((byte) (firstVisibileChildAtMinimumHeight ? 1 : 0));
1128e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1129e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1130e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            public static final Parcelable.Creator<SavedState> CREATOR =
113162ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes                    ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
1132e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                        @Override
113362ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes                        public SavedState createFromParcel(Parcel source, ClassLoader loader) {
113462ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes                            return new SavedState(source, loader);
1135e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                        }
1136e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1137e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                        @Override
1138e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                        public SavedState[] newArray(int size) {
1139e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                            return new SavedState[size];
1140e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                        }
114162ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes                    });
1142a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1143a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
1144a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1145a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
1146a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Behavior which should be used by {@link View}s which can scroll vertically and support
1147a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * nested scrolling to automatically scroll any {@link AppBarLayout} siblings.
1148a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
1149ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    public static class ScrollingViewBehavior extends HeaderScrollingViewBehavior {
1150a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1151a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior() {}
1152a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1153a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior(Context context, AttributeSet attrs) {
1154a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
1155a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1156b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            final TypedArray a = context.obtainStyledAttributes(attrs,
1157a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    R.styleable.ScrollingViewBehavior_Params);
1158b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            setOverlayTop(a.getDimensionPixelSize(
1159b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                    R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0));
1160a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
1161a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1162a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1163a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
1164a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
1165a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // We depend on any AppBarLayouts
1166a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return dependency instanceof AppBarLayout;
1167a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1168a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1169a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
1170a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
1171a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View dependency) {
11725e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes            offsetChildAsNeeded(parent, child, dependency);
11730d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes            return false;
11740d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes        }
11750d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes
11765e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes        private void offsetChildAsNeeded(CoordinatorLayout parent, View child, View dependency) {
1177a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final CoordinatorLayout.Behavior behavior =
1178a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
1179a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (behavior instanceof Behavior) {
11805e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                // Offset the child, pinning it to the bottom the header-dependency, maintaining
11815e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                // any vertical gap, and overlap
11825e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                final Behavior ablBehavior = (Behavior) behavior;
11835e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                final int offset = ablBehavior.getTopBottomOffsetForScrollingSibling();
11845e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                child.offsetTopAndBottom((dependency.getBottom() - child.getTop())
11855e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                        + ablBehavior.mOffsetDelta
11865e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                        + getVerticalLayoutGap()
1187b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                        - getOverlapPixelsForOffset(dependency));
1188ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            }
1189ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        }
1190ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
1191b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes
1192b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes        @Override
1193b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes        float getOverlapRatioForOffset(final View header) {
1194b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            if (header instanceof AppBarLayout) {
1195b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                final AppBarLayout abl = (AppBarLayout) header;
1196ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                final int totalScrollRange = abl.getTotalScrollRange();
1197ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                final int preScrollDown = abl.getDownNestedPreScrollRange();
1198b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                final int offset = getAppBarLayoutOffset(abl);
1199a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1200ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                if (preScrollDown != 0 && (totalScrollRange + offset) <= preScrollDown) {
1201ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    // If we're in a pre-scroll down. Don't use the offset at all.
1202ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    return 0;
1203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
1204ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    final int availScrollRange = totalScrollRange - preScrollDown;
1205dfb857dd12b359c0bdae11a23e039f286d54dc0aChris Banes                    if (availScrollRange != 0) {
1206ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                        // Else we'll use a interpolated ratio of the overlap, depending on offset
1207b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                        return 1f + (offset / (float) availScrollRange);
1208ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    }
1209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
1210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1211b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            return 0f;
1212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1214b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes        private static int getAppBarLayoutOffset(AppBarLayout abl) {
1215b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            final CoordinatorLayout.Behavior behavior =
1216b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                    ((CoordinatorLayout.LayoutParams) abl.getLayoutParams()).getBehavior();
1217b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            if (behavior instanceof Behavior) {
1218b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                return ((Behavior) behavior).getTopBottomOffsetForScrollingSibling();
1219b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            }
1220b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            return 0;
1221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1223ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
122472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        View findFirstDependency(List<View> views) {
1225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = views.size(); i < z; i++) {
1226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View view = views.get(i);
1227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (view instanceof AppBarLayout) {
122872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    return view;
1229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
1230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return null;
1232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1233ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
1234ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
123572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getScrollRange(View v) {
1236ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            if (v instanceof AppBarLayout) {
1237ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                return ((AppBarLayout) v).getTotalScrollRange();
1238ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            } else {
1239ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                return super.getScrollRange(v);
1240ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            }
1241ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        }
1242a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
1243a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes}
1244