AppBarLayout.java revision b75a16fb6433ab27973d419d23ed1286c073fca8
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;
211a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banesimport android.os.Build;
22e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcel;
23e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcelable;
2450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport android.support.annotation.IntDef;
2572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.annotation.NonNull;
2672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.annotation.Nullable;
27a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.design.R;
2862ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banesimport android.support.v4.os.ParcelableCompat;
2962ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banesimport android.support.v4.os.ParcelableCompatCreatorCallbacks;
30a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat;
316ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banesimport android.support.v4.view.WindowInsetsCompat;
32a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet;
33a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View;
34a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup;
35a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator;
36a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout;
37a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
3850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.Retention;
3950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.RetentionPolicy;
40d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banesimport java.lang.ref.WeakReference;
41631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.ArrayList;
42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List;
43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/**
45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of
469fb154338a62edc2c57dc036895199d6f1769400Chris Banes * material designs app bar concept, namely scrolling gestures.
47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through
49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute:
50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}.
51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}.
54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will
55a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work.
56a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
579fb154338a62edc2c57dc036895199d6f1769400Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to know when to scroll.
589fb154338a62edc2c57dc036895199d6f1769400Chris Banes * The binding is done through the {@link ScrollingViewBehavior} behavior class, meaning that you
599fb154338a62edc2c57dc036895199d6f1769400Chris Banes * should set your scrolling view's behavior to be an instance of {@link ScrollingViewBehavior}.
609fb154338a62edc2c57dc036895199d6f1769400Chris Banes * A string resource containing the full class name is available.
61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre>
63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;android.support.design.widget.CoordinatorLayout
64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:android=&quot;http://schemas.android.com/apk/res/android";
65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:app=&quot;http://schemas.android.com/apk/res-auto";
66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_width=&quot;match_parent&quot;
67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_height=&quot;match_parent&quot;&gt;
68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.v4.widget.NestedScrollView
70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;
71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;match_parent&quot;
72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             app:layout_behavior=&quot;@string/appbar_scrolling_view_behavior&quot;&gt;
73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;!-- Your scrolling content --&gt;
75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.v4.widget.NestedScrollView&gt;
77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.design.widget.AppBarLayout
79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;wrap_content&quot;
80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;&gt;
81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.v7.widget.Toolbar
83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.design.widget.TabLayout
87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.design.widget.AppBarLayout&gt;
91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;/android.support.design.widget.CoordinatorLayout&gt;
93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre>
94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar">
96a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a>
97a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */
98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
99a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout {
100a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1019995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    private static final int PENDING_ACTION_NONE = 0x0;
1029995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    private static final int PENDING_ACTION_EXPANDED = 0x1;
1039995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    private static final int PENDING_ACTION_COLLAPSED = 0x2;
1049995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    private static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4;
1059995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
107631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical
108631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * offset changes.
109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
110631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public interface OnOffsetChangedListener {
111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows
113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * child views to implement custom behavior based on the offset (for instance pinning a
114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * view at a certain y value).
115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
116631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes         * @param appBarLayout the {@link AppBarLayout} which offset has changed
11750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes         * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px
118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
119631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset);
120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
122a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private static final int INVALID_SCROLL_RANGE = -1;
123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mTotalScrollRange = INVALID_SCROLL_RANGE;
125a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownPreScrollRange = INVALID_SCROLL_RANGE;
126a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownScrollRange = INVALID_SCROLL_RANGE;
127a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1281a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private boolean mHaveChildWithInterpolator;
12950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
1309995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    private int mPendingAction = PENDING_ACTION_NONE;
1319995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
1326ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    private WindowInsetsCompat mLastInsets;
1336ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes
1341a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private List<OnOffsetChangedListener> mListeners;
1351a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
1361a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private boolean mCollapsible;
1371a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private boolean mCollapsed;
1381a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
1391a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private final int[] mTmpStatesArray = new int[2];
140631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
141a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context) {
142a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        this(context, null);
143a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
144a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
145a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context, AttributeSet attrs) {
146a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super(context, attrs);
147a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        setOrientation(VERTICAL);
14850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
149809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes        ThemeUtils.checkAppCompatTheme(context);
150809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes
1511a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (Build.VERSION.SDK_INT >= 21) {
1521a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            // Use the bounds view outline provider so that we cast a shadow, even without a
1531a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            // background
1541a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            ViewUtilsLollipop.setBoundsViewOutlineProvider(this);
1551a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
1561a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            // If we're running on API 21+, we should reset any state list animator from our
1571a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            // default style
1581a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            ViewUtilsLollipop.setStateListAnimatorFromAttrs(this, attrs, 0,
1591a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    R.style.Widget_Design_AppBarLayout);
1601a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
1611a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
1621a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout,
16350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                0, R.style.Widget_Design_AppBarLayout);
16481520564f3dd783136e025174021ba4eabd6ff3cChris Banes        setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background));
1659995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        if (a.hasValue(R.styleable.AppBarLayout_expanded)) {
1669995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false));
1679995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        }
1681a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (Build.VERSION.SDK_INT >= 21 && a.hasValue(R.styleable.AppBarLayout_elevation)) {
1691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(
1701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    this, a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0));
1711a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
17250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        a.recycle();
17350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
1746ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        ViewCompat.setOnApplyWindowInsetsListener(this,
1756ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                new android.support.v4.view.OnApplyWindowInsetsListener() {
1766ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    @Override
1776ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    public WindowInsetsCompat onApplyWindowInsets(View v,
1786ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                            WindowInsetsCompat insets) {
1798818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes                        return onWindowInsetChanged(insets);
1806ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    }
1816ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                });
182631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
183631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
184631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
185631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Add a listener that will be called when the offset of this {@link AppBarLayout} changes.
186631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
187631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param listener The listener that will be called when the offset changes.]
188631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
189631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @see #removeOnOffsetChangedListener(OnOffsetChangedListener)
190631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
191631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void addOnOffsetChangedListener(OnOffsetChangedListener listener) {
1921a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (mListeners == null) {
1931a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            mListeners = new ArrayList<>();
1941a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
1956f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes        if (listener != null && !mListeners.contains(listener)) {
1966f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes            mListeners.add(listener);
197631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
198631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
199631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
200631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
201631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Remove the previously added {@link OnOffsetChangedListener}.
202631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
203631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param listener the listener to remove.
204631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
205631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) {
2061a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (mListeners != null && listener != null) {
2076f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes            mListeners.remove(listener);
208631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
212ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
213ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
214ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        invalidateScrollRanges();
215ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    }
216ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
217ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    @Override
218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected void onLayout(boolean changed, int l, int t, int r, int b) {
219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.onLayout(changed, l, t, r, b);
22079aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes        invalidateScrollRanges();
221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mHaveChildWithInterpolator = false;
223a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
224a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams childLp = (LayoutParams) child.getLayoutParams();
226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final Interpolator interpolator = childLp.getScrollInterpolator();
227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (interpolator != null) {
229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mHaveChildWithInterpolator = true;
230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
2331a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
2341a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        updateCollapsible();
2351a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    }
2361a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
2371a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private void updateCollapsible() {
2381a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        boolean haveCollapsibleChild = false;
2391a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
2401a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            if (((LayoutParams) getChildAt(i).getLayoutParams()).isCollapsible()) {
2411a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                haveCollapsibleChild = true;
2421a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                break;
2431a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            }
2441a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
2451a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        setCollapsible(haveCollapsibleChild);
246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
248ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private void invalidateScrollRanges() {
249ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        // Invalidate the scroll ranges
250ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        mTotalScrollRange = INVALID_SCROLL_RANGE;
251ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        mDownPreScrollRange = INVALID_SCROLL_RANGE;
252ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        mDownScrollRange = INVALID_SCROLL_RANGE;
253ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    }
254ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
255a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
256a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public void setOrientation(int orientation) {
257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (orientation != VERTICAL) {
258a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            throw new IllegalArgumentException("AppBarLayout is always vertical and does"
259a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    + " not support horizontal orientation");
260a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.setOrientation(orientation);
262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
2649995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    /**
2659995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * Sets whether this {@link AppBarLayout} is expanded or not, animating if it has already
2669995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * been laid out.
2679995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2689995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a
2699995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * direct child of a {@link CoordinatorLayout}.</p>
2709995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2719995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @param expanded true if the layout should be fully expanded, false if it should
2729995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *                 be fully collapsed
2739995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2749995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @attr ref android.support.design.R.styleable#AppBarLayout_expanded
2759995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     */
2769995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    public void setExpanded(boolean expanded) {
2779995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        setExpanded(expanded, ViewCompat.isLaidOut(this));
2789995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
2799995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
2809995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    /**
2819995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * Sets whether this {@link AppBarLayout} is expanded or not.
2829995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2839995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a
2849995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * direct child of a {@link CoordinatorLayout}.</p>
2859995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2869995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @param expanded true if the layout should be fully expanded, false if it should
2879995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *                 be fully collapsed
2889995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @param animate Whether to animate to the new state
2899995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     *
2909995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     * @attr ref android.support.design.R.styleable#AppBarLayout_expanded
2919995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes     */
2929995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    public void setExpanded(boolean expanded, boolean animate) {
2939995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        mPendingAction = (expanded ? PENDING_ACTION_EXPANDED : PENDING_ACTION_COLLAPSED)
2949995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                | (animate ? PENDING_ACTION_ANIMATE_ENABLED : 0);
2959995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        requestLayout();
2969995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
2979995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return p instanceof LayoutParams;
301a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateDefaultLayoutParams() {
305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public LayoutParams generateLayoutParams(AttributeSet attrs) {
310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(getContext(), attrs);
311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (p instanceof LinearLayout.LayoutParams) {
316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((LinearLayout.LayoutParams) p);
317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        } else if (p instanceof MarginLayoutParams) {
318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((MarginLayoutParams) p);
319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(p);
321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
323ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private boolean hasChildWithInterpolator() {
324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mHaveChildWithInterpolator;
325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
3286ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes     * Returns the scroll range of all children.
329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     *
330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * @return the scroll range in px
331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
3326ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    public final int getTotalScrollRange() {
333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mTotalScrollRange != INVALID_SCROLL_RANGE) {
334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mTotalScrollRange;
335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
341ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            final int childHeight = child.getMeasuredHeight();
342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
346b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                range += childHeight + lp.topMargin + lp.bottomMargin;
347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing scroll, we to take the collapsed height into account.
3506ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    // We also break straight away since later views can't scroll beneath
351a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // us
3526ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    range -= ViewCompat.getMinimumHeight(child);
3536ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    break;
354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
3614832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes        return mTotalScrollRange = Math.max(0, range - getTopInset());
362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
364ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private boolean hasScrollableChildren() {
365a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return getTotalScrollRange() != 0;
366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
368a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
369a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling up from a nested pre-scroll.
370a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
371ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private int getUpNestedPreScrollRange() {
37250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        return getTotalScrollRange();
373a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested pre-scroll.
377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
378ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private int getDownNestedPreScrollRange() {
379a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownPreScrollRange != INVALID_SCROLL_RANGE) {
380a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownPreScrollRange;
382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
384a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
385a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = getChildCount() - 1; i >= 0; i--) {
386a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
387a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
388ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            final int childHeight = child.getMeasuredHeight();
389a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
390a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
391a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) {
392b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                // First take the margin into account
393b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                range += lp.topMargin + lp.bottomMargin;
394a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // The view has the quick return flag combination...
395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) {
396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If they're set to enter collapsed, use the minimum height
397a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    range += ViewCompat.getMinimumHeight(child);
39847543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                } else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
39947543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    // Only enter by the amount of the collapsed height
40047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    range += childHeight - ViewCompat.getMinimumHeight(child);
401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Else use the full height
403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    range += childHeight;
404a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
405a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else if (range > 0) {
406a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // If we've hit an non-quick return scrollable view, and we've already hit a
407a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // quick return view, return now
408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
411090293b68e5aad0af0a0d8ae079ac0df743ff3ecChris Banes        return mDownPreScrollRange = Math.max(0, range);
412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested scroll.
416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
417ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private int getDownNestedScrollRange() {
418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownScrollRange != INVALID_SCROLL_RANGE) {
419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
420a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownScrollRange;
421a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
422a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
423a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
424c1ce4f6cf4d99d6856c4259bf1bb1cb56f604737Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
425a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
426a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
427ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            int childHeight = child.getMeasuredHeight();
428b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes            childHeight += lp.topMargin + lp.bottomMargin;
429a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
430a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
431a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
432a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
433a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
434a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                range += childHeight;
435a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
436a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
437a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing exit scroll, we to take the collapsed height into account.
4384832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes                    // We also break the range straight away since later views can't scroll
439a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // beneath us
4404832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes                    range -= ViewCompat.getMinimumHeight(child) + getTopInset();
4414832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes                    break;
442a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
443a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
444a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
4494c33be829067714342b629d29329206bc2116afeChris Banes        return mDownScrollRange = Math.max(0, range);
450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
451a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
4521a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private void dispatchOffsetUpdates(int offset) {
4531a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        // Iterate backwards through the list so that most recently added listeners
4541a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        // get the first chance to decide
4551a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (mListeners != null) {
4561a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            for (int i = 0, z = mListeners.size(); i < z; i++) {
4571a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                final OnOffsetChangedListener listener = mListeners.get(i);
4581a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                if (listener != null) {
4591a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    listener.onOffsetChanged(this, offset);
4601a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                }
4611a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            }
4621a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
4631a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    }
4641a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
46550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    final int getMinimumHeightForVisibleOverlappingContent() {
46617ed3263761329f6aa6796941358c41001fff325Chris Banes        final int topInset = getTopInset();
46750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        final int minHeight = ViewCompat.getMinimumHeight(this);
46850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        if (minHeight != 0) {
46950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            // If this layout has a min height, use it (doubled)
4706ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes            return (minHeight * 2) + topInset;
47150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
47250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
47350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        // Otherwise, we'll use twice the min height of our last child
47450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        final int childCount = getChildCount();
475b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        final int lastChildMinHeight = childCount >= 1
476b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes                ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) : 0;
477b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        if (lastChildMinHeight != 0) {
478b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes            return (lastChildMinHeight * 2) + topInset;
479b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        }
480b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes
481b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        // If we reach here then we don't have a min height explicitly set. Instead we'll take a
482b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        // guess at 1/3 of our height being visible
483b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes        return getHeight() / 3;
48450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    }
48550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
4861a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    @Override
4871a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    protected int[] onCreateDrawableState(int extraSpace) {
4881a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        final int[] extraStates = mTmpStatesArray;
4891a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        final int[] states = super.onCreateDrawableState(extraSpace + extraStates.length);
4901a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
4911a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        extraStates[0] = mCollapsible ? R.attr.state_collapsible : -R.attr.state_collapsible;
4921a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        extraStates[1] = mCollapsible && mCollapsed
4931a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                ? R.attr.state_collapsed : -R.attr.state_collapsed;
4941a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
4951a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        return mergeDrawableStates(states, extraStates);
4961a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    }
4971a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
4981a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private void setCollapsible(boolean collapsible) {
4991a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (mCollapsible != collapsible) {
5001a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            mCollapsible = collapsible;
5011a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            refreshDrawableState();
5021a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
5031a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    }
5041a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
5051a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    private void setCollapsed(boolean collapsed) {
5061a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (mCollapsed != collapsed) {
5071a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            mCollapsed = collapsed;
5081a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            refreshDrawableState();
5091a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
5101a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    }
5111a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
51250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    /**
5131a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes     * @deprecated target elevation is now deprecated. AppBarLayout's elevation is now
514926cd28257536b1206934d69585a2dab100147d5Chris Banes     * controlled via a {@link android.animation.StateListAnimator}. If a target
515926cd28257536b1206934d69585a2dab100147d5Chris Banes     * elevation is set, either by this method or the {@code app:elevation} attibute,
516926cd28257536b1206934d69585a2dab100147d5Chris Banes     * a new state list animator is created which uses the given {@code elevation} value.
517631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
5181a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes     * @attr ref android.support.design.R.styleable#AppBarLayout_elevation
51950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes     */
5201a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    @Deprecated
521631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void setTargetElevation(float elevation) {
5221a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        if (Build.VERSION.SDK_INT >= 21) {
5231a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(this, elevation);
5241a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
525631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
526631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
527631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
5281a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes     * @deprecated target elevation is now deprecated. AppBarLayout's elevation is now
529926cd28257536b1206934d69585a2dab100147d5Chris Banes     * controlled via a {@link android.animation.StateListAnimator}. This method now
530926cd28257536b1206934d69585a2dab100147d5Chris Banes     * always returns 0.
531631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
5321a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes    @Deprecated
533631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public float getTargetElevation() {
5341a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        return 0;
53550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    }
53650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
537ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private int getPendingAction() {
5389995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        return mPendingAction;
5399995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
5409995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
541ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private void resetPendingAction() {
5429995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        mPendingAction = PENDING_ACTION_NONE;
5439995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes    }
5449995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
545ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    private int getTopInset() {
546ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        return mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
547ea004a01f44aaf36212120c936952c1742d03d30Chris Banes    }
548ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
5498818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes    private WindowInsetsCompat onWindowInsetChanged(final WindowInsetsCompat insets) {
5508818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        WindowInsetsCompat newInsets = null;
5518818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes
5528818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        if (ViewCompat.getFitsSystemWindows(this)) {
5538818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            // If we're set to fit system windows, keep the insets
5548818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            newInsets = insets;
5558818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        }
5568818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes
5578818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        // If our insets have changed, keep them and invalidate the scroll ranges...
5588818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        if (newInsets != mLastInsets) {
5598818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            mLastInsets = newInsets;
5608818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes            invalidateScrollRanges();
5618818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes        }
5628818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes
56317ed3263761329f6aa6796941358c41001fff325Chris Banes        return insets;
5646ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    }
5656ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes
566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class LayoutParams extends LinearLayout.LayoutParams {
56750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
56850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        /** @hide */
56950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @IntDef(flag=true, value={
57050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_SCROLL,
57150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_EXIT_UNTIL_COLLAPSED,
57250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_ENTER_ALWAYS,
573bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED,
574bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                SCROLL_FLAG_SNAP
57550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        })
57650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Retention(RetentionPolicy.SOURCE)
57750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public @interface ScrollFlags {}
57850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
579a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
580a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * The view will be scroll in direct relation to scroll events. This flag needs to be
581a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * set for any of the other flags to take effect. If any sibling views
582a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * before this one do not have this flag, then this value has no effect.
583a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
584a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_SCROLL = 0x1;
585a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
586a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
587a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When exiting (scrolling off screen) the view will be scrolled until it is
588a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * 'collapsed'. The collapsed height is defined by the view's minimum height.
589a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
590a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
591a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
592a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
593a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2;
594a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
596a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When entering (scrolling on screen) the view will scroll on any downwards
597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * scroll event, regardless of whether the scrolling view is also scrolling. This
598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * is commonly referred to as the 'quick return' pattern.
599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
600a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4;
601a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
602a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
603a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * An additional flag for 'enterAlways' which modifies the returning view to
604a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * only initially scroll back to it's collapsed height. Once the scrolling view has
605a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * reached the end of it's scroll range, the remainder of this view will be scrolled
606a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * into view. The collapsed height is defined by the view's minimum height.
607a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
608a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
609a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
610a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
611a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8;
612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
613a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
614bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * Upon a scroll ending, if the view is only partially visible then it will be snapped
615bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * and scrolled to it's closest edge. For example, if the view only has it's bottom 25%
616bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * displayed, it will be scrolled off screen completely. Conversely, if it's bottom 75%
617bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * is visible then it will be scrolled fully into view.
618bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         */
619bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        public static final int SCROLL_FLAG_SNAP = 0x10;
620bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
621bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        /**
622bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         * Internal flags which allows quick checking features
623a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
624a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS;
625bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        static final int FLAG_SNAP = SCROLL_FLAG_SCROLL | SCROLL_FLAG_SNAP;
6261a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        static final int COLLAPSIBLE_FLAGS = SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
6271a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                | SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED;
628a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
629a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int mScrollFlags = SCROLL_FLAG_SCROLL;
630a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        Interpolator mScrollInterpolator;
631a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
632a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(Context c, AttributeSet attrs) {
633a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(c, attrs);
634a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams);
635a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0);
636a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) {
637a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int resId = a.getResourceId(
638a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0);
639a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator(
640a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        c, resId);
641a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
642a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
643a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
644a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
645a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height) {
646a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height);
647a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
648a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
649a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height, float weight) {
650a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height, weight);
651a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
652a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
653a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(ViewGroup.LayoutParams p) {
654a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(p);
655a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
656a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
657a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(MarginLayoutParams source) {
658a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
659a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
660a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
661a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LinearLayout.LayoutParams source) {
662a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
663a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
664a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
665a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LayoutParams source) {
666a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
667a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = source.mScrollFlags;
668a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = source.mScrollInterpolator;
669a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
670a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
671a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
672a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the scrolling flags.
673a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
674a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL},
675bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         *             {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS},
676bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes         *             {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED} and {@link #SCROLL_FLAG_SNAP }.
677a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
678a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollFlags()
679a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
680149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags
681a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
68250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public void setScrollFlags(@ScrollFlags int flags) {
683a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = flags;
684a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
685a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
686a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
687a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the scrolling flags.
688a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
689a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollFlags(int)
690a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
691149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags
692a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
69350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @ScrollFlags
694a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public int getScrollFlags() {
695a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollFlags;
696a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
697a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
698a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
699a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the interpolator to when scrolling the view associated with this
700a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}.
701a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
702a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling.
703a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
704149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator
705a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollInterpolator()
706a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
707a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void setScrollInterpolator(Interpolator interpolator) {
708a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = interpolator;
709a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
710a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
711a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
712a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the {@link Interpolator} being used for scrolling the view associated with this
713a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling.
714a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
715149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator
716a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollInterpolator(Interpolator)
717a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
718a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Interpolator getScrollInterpolator() {
719a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollInterpolator;
720a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
7211a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
7221a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        /**
7231a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes         * Returns true if the scroll flags are compatible for 'collapsing'
7241a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes         */
7251a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        private boolean isCollapsible() {
7261a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            return (mScrollFlags & SCROLL_FLAG_SCROLL) == SCROLL_FLAG_SCROLL
7271a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    && (mScrollFlags & COLLAPSIBLE_FLAGS) != 0;
7281a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
729a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
730a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
731a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
732a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested
733a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * scroll handling with offsetting.
734a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
735ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    public static class Behavior extends HeaderBehavior<AppBarLayout> {
736dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes        private static final int ANIMATE_OFFSET_DIPS_PER_SECOND = 300;
737e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private static final int INVALID_POSITION = -1;
738e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
73972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        /**
74072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         * Callback to allow control over any {@link AppBarLayout} dragging.
74172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         */
74272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        public static abstract class DragCallback {
74372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            /**
74472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * Allows control over whether the given {@link AppBarLayout} can be dragged or not.
74572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             *
74672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * <p>Dragging is defined as a direct touch on the AppBarLayout with movement. This
74772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * call does not affect any nested scrolling.</p>
74872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             *
74972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             * @return true if we are in a position to scroll the AppBarLayout via a drag, false
75072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             *         if not.
75172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes             */
75272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            public abstract boolean canDrag(@NonNull AppBarLayout appBarLayout);
75372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
75472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
755e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private int mOffsetDelta;
756a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
75750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        private boolean mSkipNestedPreScroll;
75847543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        private boolean mWasNestedFlung;
759631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
760c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov        private ValueAnimatorCompat mOffsetAnimator;
76150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
762e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private int mOffsetToChildIndexOnLayout = INVALID_POSITION;
763e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private boolean mOffsetToChildIndexOnLayoutIsMinHeight;
764e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        private float mOffsetToChildIndexOnLayoutPerc;
765e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
766d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        private WeakReference<View> mLastNestedScrollingChildRef;
76772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        private DragCallback mOnDragCallback;
768d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
769a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior() {}
770a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
771a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior(Context context, AttributeSet attrs) {
772a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
773a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
774a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
775a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
776ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes        public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child,
777a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View directTargetChild, View target, int nestedScrollAxes) {
778f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes            // Return true if we're nested scrolling vertically, and we have scrollable children
779f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes            // and the scrolling view is big enough to scroll
780631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0
781f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes                    && child.hasScrollableChildren()
782ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes                    && parent.getHeight() - directTargetChild.getHeight() <= child.getHeight();
783631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
784c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov            if (started && mOffsetAnimator != null) {
785631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // Cancel any offset animation
786c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                mOffsetAnimator.cancel();
787631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
788631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
789d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            // A new nested scroll has started so clear out the previous ref
790d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            mLastNestedScrollingChildRef = null;
791d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
792631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            return started;
793a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
794a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
795a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
796a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
797a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View target, int dx, int dy, int[] consumed) {
79850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            if (dy != 0 && !mSkipNestedPreScroll) {
79950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int min, max;
80050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                if (dy < 0) {
80150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // We're scrolling down
80250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    min = -child.getTotalScrollRange();
80350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    max = min + child.getDownNestedPreScrollRange();
80450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                } else {
80550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // We're scrolling up
80650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    min = -child.getUpNestedPreScrollRange();
80750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    max = 0;
80850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                }
80950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
810a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
811a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
812a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
813a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
814a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
815a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View target, int dxConsumed, int dyConsumed,
816a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int dxUnconsumed, int dyUnconsumed) {
81750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            if (dyUnconsumed < 0) {
81850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // If the scrolling view is scrolling down but not consuming, it's probably be at
81950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // the top of it's content
82050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                scroll(coordinatorLayout, child, dyUnconsumed,
82150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        -child.getDownNestedScrollRange(), 0);
82250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // Set the expanding flag so that onNestedPreScroll doesn't handle any events
82350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                mSkipNestedPreScroll = true;
82450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            } else {
82550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // As we're no longer handling nested scrolls, reset the skip flag
82650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                mSkipNestedPreScroll = false;
827a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
82850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
829a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
83050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Override
831bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl,
83250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                View target) {
83347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            if (!mWasNestedFlung) {
834bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                // If we haven't been flung then let's see if the current view has been set to snap
835bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                snapToChildIfNeeded(coordinatorLayout, abl);
836bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            }
837bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
838bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            // Reset the flags
83950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            mSkipNestedPreScroll = false;
84047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            mWasNestedFlung = false;
841d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            // Keep a reference to the previous nested scrolling child
842d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            mLastNestedScrollingChildRef = new WeakReference<>(target);
843d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        }
844d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
845d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        @Override
846631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        public boolean onNestedFling(final CoordinatorLayout coordinatorLayout,
847631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                final AppBarLayout child, View target, float velocityX, float velocityY,
848631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                boolean consumed) {
849bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            boolean flung = false;
850bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
851631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (!consumed) {
852631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // It has been consumed so let's fling ourselves
853bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                flung = fling(coordinatorLayout, child, -child.getTotalScrollRange(),
854bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        0, -velocityY);
855631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            } else {
856631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // If we're scrolling up and the child also consumed the fling. We'll fake scroll
857631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // upto our 'collapsed' offset
858631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                if (velocityY < 0) {
859631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // We're scrolling down
860bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    final int targetScroll = -child.getTotalScrollRange()
861631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                            + child.getDownNestedPreScrollRange();
862bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    if (getTopBottomOffsetForScrollingSibling() < targetScroll) {
863bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        // If we're currently not expanded more than the target scroll, we'll
864bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        // animate a fling
865bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        animateOffsetTo(coordinatorLayout, child, targetScroll);
866bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        flung = true;
867631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
868631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                } else {
869631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // We're scrolling up
870bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    final int targetScroll = -child.getUpNestedPreScrollRange();
871bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    if (getTopBottomOffsetForScrollingSibling() > targetScroll) {
872bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        // If we're currently not expanded less than the target scroll, we'll
873bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        // animate a fling
874bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        animateOffsetTo(coordinatorLayout, child, targetScroll);
875bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                        flung = true;
876631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
877631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                }
878631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
879631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
88047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            mWasNestedFlung = flung;
881bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            return flung;
882631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
883631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
88472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        /**
88572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         * Set a callback to control any {@link AppBarLayout} dragging.
88672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         *
88772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         * @param callback the callback to use, or {@code null} to use the default behavior.
88872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes         */
88972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        public void setDragCallback(@Nullable DragCallback callback) {
89072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            mOnDragCallback = callback;
89172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
89272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
893631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private void animateOffsetTo(final CoordinatorLayout coordinatorLayout,
894dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                final AppBarLayout child, final int offset) {
895dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            final int currentOffset = getTopBottomOffsetForScrollingSibling();
896dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            if (currentOffset == offset) {
897c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                if (mOffsetAnimator != null && mOffsetAnimator.isRunning()) {
898c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                    mOffsetAnimator.cancel();
899dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                }
900dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                return;
901dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            }
902dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes
903c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov            if (mOffsetAnimator == null) {
904c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                mOffsetAnimator = ViewUtils.createAnimator();
905c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                mOffsetAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
906c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                mOffsetAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
907631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    @Override
908631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    public void onAnimationUpdate(ValueAnimatorCompat animator) {
909ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                        setHeaderTopBottomOffset(coordinatorLayout, child,
910631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                                animator.getAnimatedIntValue());
911631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
912631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                });
913631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            } else {
914c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                mOffsetAnimator.cancel();
915631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
916631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
917dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            // Set the duration based on the amount of dips we're travelling in
918dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes            final float distanceDp = Math.abs(currentOffset - offset) /
919dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    coordinatorLayout.getResources().getDisplayMetrics().density;
920c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov            mOffsetAnimator.setDuration(
921c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov                    Math.round(distanceDp * 1000 / ANIMATE_OFFSET_DIPS_PER_SECOND));
922dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes
923c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov            mOffsetAnimator.setIntValues(currentOffset, offset);
924c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov            mOffsetAnimator.start();
925631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
926631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
927bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        private View getChildOnOffset(AppBarLayout abl, final int offset) {
928bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            for (int i = 0, count = abl.getChildCount(); i < count; i++) {
929bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                View child = abl.getChildAt(i);
930bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                if (child.getTop() <= -offset && child.getBottom() >= -offset) {
931bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    return child;
932bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                }
933bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            }
934bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            return null;
935bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        }
936bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
937bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        private void snapToChildIfNeeded(CoordinatorLayout coordinatorLayout, AppBarLayout abl) {
938bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            final int offset = getTopBottomOffsetForScrollingSibling();
939bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            final View offsetChild = getChildOnOffset(abl, offset);
940bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            if (offsetChild != null) {
941bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                final LayoutParams lp = (LayoutParams) offsetChild.getLayoutParams();
942bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                if ((lp.getScrollFlags() & LayoutParams.FLAG_SNAP) == LayoutParams.FLAG_SNAP) {
943bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    // We're set the snap, so animate the offset to the nearest edge
944dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    int childTop = -offsetChild.getTop();
945dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    int childBottom = -offsetChild.getBottom();
946dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes
947dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    // If the view is set only exit until it is collapsed, we'll abide by that
948dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    if ((lp.getScrollFlags() & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED)
949dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                            == LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) {
950dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                        childBottom += ViewCompat.getMinimumHeight(offsetChild);
951dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    }
952dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes
953dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                    final int newOffset = offset < (childBottom + childTop) / 2
954dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                            ? childBottom : childTop;
955bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                    animateOffsetTo(coordinatorLayout, abl,
956dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes                            MathUtils.constrain(newOffset, -abl.getTotalScrollRange(), 0));
957bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes                }
958bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes            }
959bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes        }
960bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes
961631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        @Override
962bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes        public boolean onMeasureChild(CoordinatorLayout parent, AppBarLayout child,
963bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec,
964bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                int heightUsed) {
965bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes            final CoordinatorLayout.LayoutParams lp =
966bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                    (CoordinatorLayout.LayoutParams) child.getLayoutParams();
967bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes            if (lp.height == CoordinatorLayout.LayoutParams.WRAP_CONTENT) {
968bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                // If the view is set to wrap on it's height, CoordinatorLayout by default will
969bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                // cap the view at the CoL's height. Since the AppBarLayout can scroll, this isn't
970bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                // what we actually want, so we measure it ourselves with an unspecified spec to
971bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                // allow the child to be larger than it's parent
972bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                parent.onMeasureChild(child, parentWidthMeasureSpec, widthUsed,
973bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightUsed);
974bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                return true;
975bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes            }
976bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes
977bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes            // Let the parent handle it as normal
978bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes            return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed,
979bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes                    parentHeightMeasureSpec, heightUsed);
980bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes        }
981bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes
982bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes        @Override
9839995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes        public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl,
98450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int layoutDirection) {
9859995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            boolean handled = super.onLayoutChild(parent, abl, layoutDirection);
9869995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes
9879995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            final int pendingAction = abl.getPendingAction();
9889995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            if (pendingAction != PENDING_ACTION_NONE) {
9899995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                final boolean animate = (pendingAction & PENDING_ACTION_ANIMATE_ENABLED) != 0;
9909995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                if ((pendingAction & PENDING_ACTION_COLLAPSED) != 0) {
9919995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    final int offset = -abl.getUpNestedPreScrollRange();
9929995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    if (animate) {
9939995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                        animateOffsetTo(parent, abl, offset);
9949995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    } else {
995ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                        setHeaderTopBottomOffset(parent, abl, offset);
9969995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    }
9979995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                } else if ((pendingAction & PENDING_ACTION_EXPANDED) != 0) {
9989995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    if (animate) {
9999995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                        animateOffsetTo(parent, abl, 0);
10009995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    } else {
1001ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                        setHeaderTopBottomOffset(parent, abl, 0);
10029995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                    }
10039995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                }
10049995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes            } else if (mOffsetToChildIndexOnLayout >= 0) {
10059995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes                View child = abl.getChildAt(mOffsetToChildIndexOnLayout);
1006e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                int offset = -child.getBottom();
1007e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                if (mOffsetToChildIndexOnLayoutIsMinHeight) {
1008e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    offset += ViewCompat.getMinimumHeight(child);
1009e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                } else {
1010e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc);
1011e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                }
1012e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                setTopAndBottomOffset(offset);
1013e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
101450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
101579aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes            // Finally reset any pending states
101679aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes            abl.resetPendingAction();
101779aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes            mOffsetToChildIndexOnLayout = INVALID_POSITION;
101879aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes
101947543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            // We may have changed size, so let's constrain the top and bottom offset correctly,
102047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            // just in case we're out of the bounds
102147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            setTopAndBottomOffset(
102247543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes                    MathUtils.constrain(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0));
102347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes
10241a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            // Make sure we dispatch the offset update
10251a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            abl.dispatchOffsetUpdates(getTopAndBottomOffset());
102650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
102750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            return handled;
1028a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1029a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
103072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        @Override
103172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        boolean canDragView(AppBarLayout view) {
103272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            if (mOnDragCallback != null) {
103372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                // If there is a drag callback set, it's in control
103472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                return mOnDragCallback.canDrag(view);
103572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            }
103672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
103772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            // Else we'll use the default behaviour of seeing if it can scroll down
1038d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            if (mLastNestedScrollingChildRef != null) {
103972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                // If we have a reference to a scrolling view, check it
104072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                final View scrollingView = mLastNestedScrollingChildRef.get();
104172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                return scrollingView != null && scrollingView.isShown()
104272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                        && !ViewCompat.canScrollVertically(scrollingView, -1);
104372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            } else {
104472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                // Otherwise we assume that the scrolling view hasn't been scrolled and can drag.
104572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                return true;
1046d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes            }
1047d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        }
1048d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
1049ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
105047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        void onFlingFinished(CoordinatorLayout parent, AppBarLayout layout) {
105147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            // At the end of a manual fling, check to see if we need to snap to the edge-child
105247543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes            snapToChildIfNeeded(parent, layout);
105347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        }
105447543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes
105547543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes        @Override
105672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getMaxDragOffset(AppBarLayout view) {
105772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            return -view.getDownNestedScrollRange();
105872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
105972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
106072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        @Override
106172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getScrollRangeForDragFling(AppBarLayout view) {
106272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes            return view.getTotalScrollRange();
106372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        }
106472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes
106572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        @Override
106672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int setHeaderTopBottomOffset(CoordinatorLayout coordinatorLayout,
106772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                AppBarLayout header, int newOffset, int minOffset, int maxOffset) {
1068e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            final int curOffset = getTopBottomOffsetForScrollingSibling();
1069a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            int consumed = 0;
1070a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
10715e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes            if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
1072631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // If we have some scrolling range, and we're currently within the min and max
1073631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // offsets, calculate a new offset
107450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset);
1075ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                AppBarLayout appBarLayout = (AppBarLayout) header;
1076a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (curOffset != newOffset) {
1077e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    final int interpolatedOffset = appBarLayout.hasChildWithInterpolator()
1078e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                            ? interpolateOffset(appBarLayout, newOffset)
1079e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                            : newOffset;
1080e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
10811a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    final boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset);
1082631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
1083a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update how much dy we have consumed
1084a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    consumed = curOffset - newOffset;
1085a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update the stored sibling offset
1086e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    mOffsetDelta = newOffset - interpolatedOffset;
1087a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1088a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) {
1089a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // If the offset hasn't changed and we're using an interpolated scroll
1090a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // then we need to keep any dependent views updated. CoL will do this for
1091a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // us when we move, but we need to do it manually when we don't (as an
1092a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // interpolated scroll may finish early).
1093a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        coordinatorLayout.dispatchDependentViewsChanged(appBarLayout);
1094a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
1095a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1096631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // Dispatch the updates to any listeners
10971a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    appBarLayout.dispatchOffsetUpdates(getTopAndBottomOffset());
10981a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
10991a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    // Update the AppBarLayout's drawable state (for any elevation changes)
11001a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    updateAppBarLayoutDrawableState(appBarLayout, newOffset,
11011a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                            newOffset < curOffset ? -1 : 1);
1102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
11035e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes            } else {
11045e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                // Reset the offset delta
11055e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                mOffsetDelta = 0;
1106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return consumed;
1109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int interpolateOffset(AppBarLayout layout, final int offset) {
1112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int absOffset = Math.abs(offset);
1113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = layout.getChildCount(); i < z; i++) {
1115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final View child = layout.getChildAt(i);
1116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
111750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                final Interpolator interpolator = childLp.getScrollInterpolator();
1118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
111950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                if (absOffset >= child.getTop() && absOffset <= child.getBottom()) {
1120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (interpolator != null) {
112150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        int childScrollableHeight = 0;
112250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        final int flags = childLp.getScrollFlags();
112350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
1124b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                            // We're set to scroll so add the child's height plus margin
1125b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                            childScrollableHeight += child.getHeight() + childLp.topMargin
1126b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                                    + childLp.bottomMargin;
1127b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes
112850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
1129b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                                // For a collapsing scroll, we to take the collapsed height
1130b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes                                // into account.
113150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                childScrollableHeight -= ViewCompat.getMinimumHeight(child);
113250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            }
113350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        }
113450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
1135ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                        if (ViewCompat.getFitsSystemWindows(child)) {
1136ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                            childScrollableHeight -= layout.getTopInset();
1137ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                        }
1138ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
113950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        if (childScrollableHeight > 0) {
114050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            final int offsetForView = absOffset - child.getTop();
114150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            final int interpolatedDiff = Math.round(childScrollableHeight *
114250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                    interpolator.getInterpolation(
114350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                            offsetForView / (float) childScrollableHeight));
114450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
114550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            return Integer.signum(offset) * (child.getTop() + interpolatedDiff);
114650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        }
1147a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
114850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
114950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // If we get to here then the view on the offset isn't suitable for interpolated
115050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // scrolling. So break out of the loop
115150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    break;
1152a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
1153a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1154a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1155a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return offset;
1156a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1157a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
11581a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        private void updateAppBarLayoutDrawableState(final AppBarLayout layout,
11591a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                final int offset, final int direction) {
11601a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            final View child = getAppBarChildOnOffset(layout, offset);
11611a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            if (child != null) {
11621a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
11631a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                final int flags = childLp.getScrollFlags();
11641a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                boolean collapsed = false;
11651a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
11661a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
11671a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    final int minHeight = ViewCompat.getMinimumHeight(child);
11681a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
11691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    if (direction > 0 && (flags & (LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
11701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                            | LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED)) != 0) {
11711a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        // We're set to enter always collapsed so we are only collapsed when
11721a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        // being scrolled down, and in a collapsed offset
11731a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset();
11741a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    } else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
11751a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        // We're set to exit until collapsed, so any offset which results in
11761a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        // the minimum height (or less) being shown is collapsed
11771a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                        collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset();
11781a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    }
11791a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                }
11801a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
11811a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                layout.setCollapsed(collapsed);
11821a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            }
11831a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
11841a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
11851a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        private static View getAppBarChildOnOffset(final AppBarLayout layout, final int offset) {
11861a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            final int absOffset = Math.abs(offset);
11871a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            for (int i = 0, z = layout.getChildCount(); i < z; i++) {
11881a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                final View child = layout.getChildAt(i);
11891a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                if (absOffset >= child.getTop() && absOffset <= child.getBottom()) {
11901a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                    return child;
11911a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes                }
11921a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            }
11931a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes            return null;
11941a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes        }
11951a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes
1196ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
119772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getTopBottomOffsetForScrollingSibling() {
1198e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            return getTopAndBottomOffset() + mOffsetDelta;
1199e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        }
1200e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1201e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        @Override
1202e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout) {
1203e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            final Parcelable superState = super.onSaveInstanceState(parent, appBarLayout);
1204e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            final int offset = getTopAndBottomOffset();
1205e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1206e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            // Try and find the first visible child...
1207e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            for (int i = 0, count = appBarLayout.getChildCount(); i < count; i++) {
1208e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                View child = appBarLayout.getChildAt(i);
1209e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                final int visBottom = child.getBottom() + offset;
1210e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1211e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                if (child.getTop() + offset <= 0 && visBottom >= 0) {
1212e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    final SavedState ss = new SavedState(superState);
1213e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    ss.firstVisibleChildIndex = i;
1214e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    ss.firstVisibileChildAtMinimumHeight =
1215e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                            visBottom == ViewCompat.getMinimumHeight(child);
1216e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    ss.firstVisibileChildPercentageShown = visBottom / (float) child.getHeight();
1217e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                    return ss;
1218e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                }
1219e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1220e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1221e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            // Else we'll just return the super state
1222e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            return superState;
1223e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        }
1224e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1225e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        @Override
1226e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout,
1227e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                Parcelable state) {
1228e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            if (state instanceof SavedState) {
1229e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                final SavedState ss = (SavedState) state;
1230e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super.onRestoreInstanceState(parent, appBarLayout, ss.getSuperState());
1231e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                mOffsetToChildIndexOnLayout = ss.firstVisibleChildIndex;
1232e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                mOffsetToChildIndexOnLayoutPerc = ss.firstVisibileChildPercentageShown;
1233e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                mOffsetToChildIndexOnLayoutIsMinHeight = ss.firstVisibileChildAtMinimumHeight;
1234e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            } else {
1235e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super.onRestoreInstanceState(parent, appBarLayout, state);
1236e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                mOffsetToChildIndexOnLayout = INVALID_POSITION;
1237e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1238e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes        }
1239e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
124062ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes        protected static class SavedState extends BaseSavedState {
1241e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            int firstVisibleChildIndex;
1242e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            float firstVisibileChildPercentageShown;
1243e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            boolean firstVisibileChildAtMinimumHeight;
1244e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
124562ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes            public SavedState(Parcel source, ClassLoader loader) {
1246e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super(source);
1247e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                firstVisibleChildIndex = source.readInt();
1248e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                firstVisibileChildPercentageShown = source.readFloat();
1249e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                firstVisibileChildAtMinimumHeight = source.readByte() != 0;
1250e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1251e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1252e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            public SavedState(Parcelable superState) {
1253e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super(superState);
1254e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1255e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1256e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            @Override
1257e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            public void writeToParcel(Parcel dest, int flags) {
1258e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                super.writeToParcel(dest, flags);
1259e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                dest.writeInt(firstVisibleChildIndex);
1260e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                dest.writeFloat(firstVisibileChildPercentageShown);
1261e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                dest.writeByte((byte) (firstVisibileChildAtMinimumHeight ? 1 : 0));
1262e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            }
1263e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1264e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes            public static final Parcelable.Creator<SavedState> CREATOR =
126562ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes                    ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
1266e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                        @Override
126762ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes                        public SavedState createFromParcel(Parcel source, ClassLoader loader) {
126862ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes                            return new SavedState(source, loader);
1269e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                        }
1270e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes
1271e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                        @Override
1272e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                        public SavedState[] newArray(int size) {
1273e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                            return new SavedState[size];
1274e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes                        }
127562ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes                    });
1276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
1278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
1280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Behavior which should be used by {@link View}s which can scroll vertically and support
1281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * nested scrolling to automatically scroll any {@link AppBarLayout} siblings.
1282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
1283ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor    public static class ScrollingViewBehavior extends HeaderScrollingViewBehavior {
1284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior() {}
1286a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior(Context context, AttributeSet attrs) {
1288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
1289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1290b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            final TypedArray a = context.obtainStyledAttributes(attrs,
1291a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    R.styleable.ScrollingViewBehavior_Params);
1292b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            setOverlayTop(a.getDimensionPixelSize(
1293b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                    R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0));
1294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
1295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1296a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
1298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
1299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // We depend on any AppBarLayouts
1300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return dependency instanceof AppBarLayout;
1301a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
1304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
1305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View dependency) {
13065e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes            offsetChildAsNeeded(parent, child, dependency);
13070d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes            return false;
13080d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes        }
13090d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes
13105e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes        private void offsetChildAsNeeded(CoordinatorLayout parent, View child, View dependency) {
1311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final CoordinatorLayout.Behavior behavior =
1312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
1313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (behavior instanceof Behavior) {
13145e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                // Offset the child, pinning it to the bottom the header-dependency, maintaining
13155e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                // any vertical gap, and overlap
13165e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                final Behavior ablBehavior = (Behavior) behavior;
13175e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                final int offset = ablBehavior.getTopBottomOffsetForScrollingSibling();
1318318baf84dade07174d71e10322e3b10ab4b0c28cChris Banes                ViewCompat.offsetTopAndBottom(child, (dependency.getBottom() - child.getTop())
13195e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                        + ablBehavior.mOffsetDelta
13205e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes                        + getVerticalLayoutGap()
1321b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                        - getOverlapPixelsForOffset(dependency));
1322ea004a01f44aaf36212120c936952c1742d03d30Chris Banes            }
1323ea004a01f44aaf36212120c936952c1742d03d30Chris Banes        }
1324ea004a01f44aaf36212120c936952c1742d03d30Chris Banes
1325b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes
1326b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes        @Override
1327b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes        float getOverlapRatioForOffset(final View header) {
1328b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            if (header instanceof AppBarLayout) {
1329b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                final AppBarLayout abl = (AppBarLayout) header;
1330ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                final int totalScrollRange = abl.getTotalScrollRange();
1331ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                final int preScrollDown = abl.getDownNestedPreScrollRange();
1332b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                final int offset = getAppBarLayoutOffset(abl);
1333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1334ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                if (preScrollDown != 0 && (totalScrollRange + offset) <= preScrollDown) {
1335ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    // If we're in a pre-scroll down. Don't use the offset at all.
1336ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    return 0;
1337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
1338ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    final int availScrollRange = totalScrollRange - preScrollDown;
1339dfb857dd12b359c0bdae11a23e039f286d54dc0aChris Banes                    if (availScrollRange != 0) {
1340ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                        // Else we'll use a interpolated ratio of the overlap, depending on offset
1341b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                        return 1f + (offset / (float) availScrollRange);
1342ea004a01f44aaf36212120c936952c1742d03d30Chris Banes                    }
1343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
1344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1345b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            return 0f;
1346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1348b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes        private static int getAppBarLayoutOffset(AppBarLayout abl) {
1349b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            final CoordinatorLayout.Behavior behavior =
1350b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                    ((CoordinatorLayout.LayoutParams) abl.getLayoutParams()).getBehavior();
1351b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            if (behavior instanceof Behavior) {
1352b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes                return ((Behavior) behavior).getTopBottomOffsetForScrollingSibling();
1353b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            }
1354b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes            return 0;
1355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1357ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
135872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        View findFirstDependency(List<View> views) {
1359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = views.size(); i < z; i++) {
1360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View view = views.get(i);
1361a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (view instanceof AppBarLayout) {
136272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes                    return view;
1363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
1364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
1365a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return null;
1366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
1367ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor
1368ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        @Override
136972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes        int getScrollRange(View v) {
1370ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            if (v instanceof AppBarLayout) {
1371ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                return ((AppBarLayout) v).getTotalScrollRange();
1372ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            } else {
1373ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor                return super.getScrollRange(v);
1374ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor            }
1375ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor        }
1376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
1377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes}
1378