AppBarLayout.java revision 6ba61c5c79fe025036593c9daf79cb65299bb0b6
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;
2150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport android.support.annotation.IntDef;
22a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.design.R;
23a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat;
246ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banesimport android.support.v4.view.WindowInsetsCompat;
25631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport android.support.v4.widget.ScrollerCompat;
26a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet;
27a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View;
28a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup;
29a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator;
30a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout;
31a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
3250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.Retention;
3350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.RetentionPolicy;
34631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.lang.ref.WeakReference;
35631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.ArrayList;
36631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.Iterator;
37a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List;
38a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
39a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/**
40a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of
41631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * material design's app bar concept, namely scrolling gestures.
42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through
44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute:
45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}.
46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}.
49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will
50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work.
51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to. The binding is done through
53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the {@link ScrollingViewBehavior} beahior class, meaning that you should set your scrolling
54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view's behavior to be an instance of {@link ScrollingViewBehavior}. A string resource containing
55a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the full class name is available.
56a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
57a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre>
58a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;android.support.design.widget.CoordinatorLayout
59a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:android=&quot;http://schemas.android.com/apk/res/android";
60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:app=&quot;http://schemas.android.com/apk/res-auto";
61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_width=&quot;match_parent&quot;
62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_height=&quot;match_parent&quot;&gt;
63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.v4.widget.NestedScrollView
65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;
66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;match_parent&quot;
67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             app:layout_behavior=&quot;@string/appbar_scrolling_view_behavior&quot;&gt;
68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;!-- Your scrolling content --&gt;
70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.v4.widget.NestedScrollView&gt;
72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.design.widget.AppBarLayout
74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;wrap_content&quot;
75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;&gt;
76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.v7.widget.Toolbar
78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.design.widget.TabLayout
82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.design.widget.AppBarLayout&gt;
86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;/android.support.design.widget.CoordinatorLayout&gt;
88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre>
89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar">
91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a>
92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */
93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout {
95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
96a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
97631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical
98631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * offset changes.
99a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
100631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public interface OnOffsetChangedListener {
101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows
103a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * child views to implement custom behavior based on the offset (for instance pinning a
104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * view at a certain y value).
105a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
106631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes         * @param appBarLayout the {@link AppBarLayout} which offset has changed
10750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes         * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px
108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
109631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset);
110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private static final int INVALID_SCROLL_RANGE = -1;
113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mTotalScrollRange = INVALID_SCROLL_RANGE;
115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownPreScrollRange = INVALID_SCROLL_RANGE;
116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownScrollRange = INVALID_SCROLL_RANGE;
117a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    boolean mHaveChildWithInterpolator;
119a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
12050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    private float mTargetElevation;
12150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
1226ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    private WindowInsetsCompat mLastInsets;
1236ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes
124631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    private final List<WeakReference<OnOffsetChangedListener>> mListeners;
125631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
126a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context) {
127a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        this(context, null);
128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
129a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
130a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context, AttributeSet attrs) {
131a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super(context, attrs);
132a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        setOrientation(VERTICAL);
13350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
13450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout,
13550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                0, R.style.Widget_Design_AppBarLayout);
13650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0);
13781520564f3dd783136e025174021ba4eabd6ff3cChris Banes        setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background));
13850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        a.recycle();
13950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
14050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        // Use the bounds view outline provider so that we cast a shadow, even without a background
14150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        ViewUtils.setBoundsViewOutlineProvider(this);
142631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
143631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        mListeners = new ArrayList<>();
144631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
145631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        ViewCompat.setElevation(this, mTargetElevation);
1466ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes
1476ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        ViewCompat.setOnApplyWindowInsetsListener(this,
1486ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                new android.support.v4.view.OnApplyWindowInsetsListener() {
1496ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    @Override
1506ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    public WindowInsetsCompat onApplyWindowInsets(View v,
1516ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                            WindowInsetsCompat insets) {
1526ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                        setWindowInsets(insets);
1536ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                        return insets.consumeSystemWindowInsets();
1546ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    }
1556ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                });
156631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
157631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
158631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
159631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Add a listener that will be called when the offset of this {@link AppBarLayout} changes.
160631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
161631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param listener The listener that will be called when the offset changes.]
162631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
163631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @see #removeOnOffsetChangedListener(OnOffsetChangedListener)
164631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
165631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void addOnOffsetChangedListener(OnOffsetChangedListener listener) {
166631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        for (int i = 0, z = mListeners.size(); i < z; i++) {
167631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final WeakReference<OnOffsetChangedListener> ref = mListeners.get(i);
168631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (ref != null && ref.get() == listener) {
169631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // Listener already added
170631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                return;
171631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
172631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
173631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        mListeners.add(new WeakReference<>(listener));
174631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
175631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
176631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
177631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Remove the previously added {@link OnOffsetChangedListener}.
178631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
179631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param listener the listener to remove.
180631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
181631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) {
182631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        final Iterator<WeakReference<OnOffsetChangedListener>> i = mListeners.iterator();
183631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        while (i.hasNext()) {
184631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final WeakReference<OnOffsetChangedListener> ref = i.next();
185631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final OnOffsetChangedListener item = ref.get();
186631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (item == listener || item == null) {
187631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // If the item is null, or is our given listener, remove
188631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                i.remove();
189631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
190631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
191a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
192a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
193a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
194a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected void onLayout(boolean changed, int l, int t, int r, int b) {
195a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.onLayout(changed, l, t, r, b);
196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        // Invalidate the scroll ranges
198a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mTotalScrollRange = INVALID_SCROLL_RANGE;
199a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mDownPreScrollRange = INVALID_SCROLL_RANGE;
200a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mDownPreScrollRange = INVALID_SCROLL_RANGE;
201a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mHaveChildWithInterpolator = false;
203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams childLp = (LayoutParams) child.getLayoutParams();
206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final Interpolator interpolator = childLp.getScrollInterpolator();
207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (interpolator != null) {
209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mHaveChildWithInterpolator = true;
210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
216a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public void setOrientation(int orientation) {
217a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (orientation != VERTICAL) {
218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            throw new IllegalArgumentException("AppBarLayout is always vertical and does"
219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    + " not support horizontal orientation");
220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.setOrientation(orientation);
222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
223a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
224a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return p instanceof LayoutParams;
227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateDefaultLayoutParams() {
231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
234a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
235a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public LayoutParams generateLayoutParams(AttributeSet attrs) {
236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(getContext(), attrs);
237a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
238a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
239a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
240a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
241a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (p instanceof LinearLayout.LayoutParams) {
242a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((LinearLayout.LayoutParams) p);
243a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        } else if (p instanceof MarginLayoutParams) {
244a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((MarginLayoutParams) p);
245a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(p);
247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
248a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
249a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final boolean hasChildWithInterpolator() {
250a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mHaveChildWithInterpolator;
251a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
252a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
253a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
2546ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes     * Returns the scroll range of all children.
255a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     *
256a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * @return the scroll range in px
257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
2586ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    public final int getTotalScrollRange() {
259a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mTotalScrollRange != INVALID_SCROLL_RANGE) {
260a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mTotalScrollRange;
261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
264a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
265a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
266a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
267a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int childHeight = ViewCompat.isLaidOut(child)
268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ? child.getHeight()
269a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    : child.getMeasuredHeight();
270a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                range += childHeight;
275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing scroll, we to take the collapsed height into account.
2786ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    // We also break straight away since later views can't scroll beneath
279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // us
2806ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    range -= ViewCompat.getMinimumHeight(child);
2816ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    break;
282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
283a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
286a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
2896ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        final int top = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
2906ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        return mTotalScrollRange = (range - top);
291a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
292a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
293a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final boolean hasScrollableChildren() {
294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return getTotalScrollRange() != 0;
295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
296a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling up from a nested pre-scroll.
299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getUpNestedPreScrollRange() {
30150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        return getTotalScrollRange();
302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested pre-scroll.
306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getDownNestedPreScrollRange() {
308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownPreScrollRange != INVALID_SCROLL_RANGE) {
309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownPreScrollRange;
311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = getChildCount() - 1; i >= 0; i--) {
315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int childHeight = ViewCompat.isLaidOut(child)
318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ? child.getHeight()
319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    : child.getMeasuredHeight();
320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) {
323a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // The view has the quick return flag combination...
324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) {
325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If they're set to enter collapsed, use the minimum height
326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    range += ViewCompat.getMinimumHeight(child);
327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Else use the full height
329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    range += childHeight;
330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else if (range > 0) {
332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // If we've hit an non-quick return scrollable view, and we've already hit a
333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // quick return view, return now
334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mDownPreScrollRange = range;
338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested scroll.
342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getDownNestedScrollRange() {
344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownScrollRange != INVALID_SCROLL_RANGE) {
345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownScrollRange;
347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
350a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = getChildCount() - 1; i >= 0; i--) {
351a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
352a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int childHeight = ViewCompat.isLaidOut(child)
354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ? child.getHeight()
355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    : child.getMeasuredHeight();
356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
361a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                range += childHeight;
362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing exit scroll, we to take the collapsed height into account.
365a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // We also return the range straight away since later views can't scroll
366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // beneath us
367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return range - ViewCompat.getMinimumHeight(child);
368a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
369a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
370a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
371a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
372a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
373a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mDownScrollRange = range;
376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
37850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    final int getMinimumHeightForVisibleOverlappingContent() {
3796ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        final int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
38050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        final int minHeight = ViewCompat.getMinimumHeight(this);
38150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        if (minHeight != 0) {
38250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            // If this layout has a min height, use it (doubled)
3836ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes            return (minHeight * 2) + topInset;
38450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
38550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
38650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        // Otherwise, we'll use twice the min height of our last child
38750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        final int childCount = getChildCount();
38850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        return childCount >= 1
3896ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                ? (ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2) + topInset
39050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                : 0;
39150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    }
39250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
39350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    /**
394631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Set the elevation value to use when this {@link AppBarLayout} should be elevated
395631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * above content.
396631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * <p>
397631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * This method does not do anything itself. A typical use for this method is called from within
398631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * an {@link OnOffsetChangedListener} when the offset has changed in such a way to require an
399631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * elevation change.
400631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
401631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param elevation the elevation value to use.
402631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
403631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @see ViewCompat#setElevation(View, float)
40450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes     */
405631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void setTargetElevation(float elevation) {
406631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        mTargetElevation = elevation;
407631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
408631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
409631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
410631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Returns the elevation value to use when this {@link AppBarLayout} should be elevated
411631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * above content.
412631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
413631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public float getTargetElevation() {
41450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        return mTargetElevation;
41550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    }
41650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
4176ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    private void setWindowInsets(WindowInsetsCompat insets) {
4186ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        // Invalidate the total scroll range...
4196ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        mTotalScrollRange = INVALID_SCROLL_RANGE;
4206ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        mLastInsets = insets;
4216ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes
4226ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        // Now dispatch them to our children
4236ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
4246ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes            final View child = getChildAt(i);
4256ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes            insets = ViewCompat.dispatchApplyWindowInsets(child, insets);
4266ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes            if (insets.isConsumed()) {
4276ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                break;
4286ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes            }
4296ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes        }
4306ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes    }
4316ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes
432a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class LayoutParams extends LinearLayout.LayoutParams {
43350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
43450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        /** @hide */
43550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @IntDef(flag=true, value={
43650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_SCROLL,
43750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_EXIT_UNTIL_COLLAPSED,
43850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_ENTER_ALWAYS,
43950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED
44050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        })
44150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Retention(RetentionPolicy.SOURCE)
44250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public @interface ScrollFlags {}
44350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
444a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * The view will be scroll in direct relation to scroll events. This flag needs to be
446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * set for any of the other flags to take effect. If any sibling views
447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * before this one do not have this flag, then this value has no effect.
448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
449a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_SCROLL = 0x1;
450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
451a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
452a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When exiting (scrolling off screen) the view will be scrolled until it is
453a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * 'collapsed'. The collapsed height is defined by the view's minimum height.
454a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
455a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
456a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
457a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
458a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2;
459a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
460a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
461a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When entering (scrolling on screen) the view will scroll on any downwards
462a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * scroll event, regardless of whether the scrolling view is also scrolling. This
463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * is commonly referred to as the 'quick return' pattern.
464a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
465a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4;
466a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
467a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
468a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * An additional flag for 'enterAlways' which modifies the returning view to
469a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * only initially scroll back to it's collapsed height. Once the scrolling view has
470a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * reached the end of it's scroll range, the remainder of this view will be scrolled
471a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * into view. The collapsed height is defined by the view's minimum height.
472a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
473a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
474a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
475a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
476a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8;
477a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
478a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
479a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Internal flag which allows quick checking of 'quick return'
480a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
481a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS;
482a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
483a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int mScrollFlags = SCROLL_FLAG_SCROLL;
484a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        Interpolator mScrollInterpolator;
485a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
486a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(Context c, AttributeSet attrs) {
487a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(c, attrs);
488a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams);
489a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0);
490a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) {
491a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int resId = a.getResourceId(
492a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0);
493a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator(
494a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        c, resId);
495a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
496a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
497a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
498a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
499a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height) {
500a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height);
501a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
502a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
503a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height, float weight) {
504a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height, weight);
505a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
506a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
507a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(ViewGroup.LayoutParams p) {
508a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(p);
509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
511a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(MarginLayoutParams source) {
512a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
513a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
514a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
515a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LinearLayout.LayoutParams source) {
516a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
517a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
519a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LayoutParams source) {
520a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
521a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = source.mScrollFlags;
522a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = source.mScrollInterpolator;
523a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
524a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
525a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
526a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the scrolling flags.
527a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
528a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL},
529a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *             {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS}
530a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *             and {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED}.
531a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
532a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollFlags()
533a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
534149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags
535a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
53650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public void setScrollFlags(@ScrollFlags int flags) {
537a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = flags;
538a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
539a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
540a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
541a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the scrolling flags.
542a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
543a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollFlags(int)
544a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
545149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags
546a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
54750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @ScrollFlags
548a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public int getScrollFlags() {
549a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollFlags;
550a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
551a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
552a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
553a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the interpolator to when scrolling the view associated with this
554a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}.
555a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
556a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling.
557a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
558149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator
559a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollInterpolator()
560a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
561a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void setScrollInterpolator(Interpolator interpolator) {
562a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = interpolator;
563a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
564a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
565a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the {@link Interpolator} being used for scrolling the view associated with this
567a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling.
568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
569149689d3a0b4165577470d7152112674d1d7f87cChris Banes         * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator
570a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollInterpolator(Interpolator)
571a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
572a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Interpolator getScrollInterpolator() {
573a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollInterpolator;
574a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
575a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
576a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
577a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
578a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested
579a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * scroll handling with offsetting.
580a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
581a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class Behavior extends ViewOffsetBehavior<AppBarLayout> {
582631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private int mLogicalOffsetTop;
583a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
58450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        private boolean mSkipNestedPreScroll;
585631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private Runnable mFlingRunnable;
586631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private ScrollerCompat mScroller;
587631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
588631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private ValueAnimatorCompat mAnimator;
58950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
590a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior() {}
591a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
592a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior(Context context, AttributeSet attrs) {
593a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
594a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
596a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View directTargetChild, View target, int nestedScrollAxes) {
599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // Return true if we're nested scrolling vertically and we have scrollable children
600631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0
601a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    && child.hasScrollableChildren();
602631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
603631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (started && mAnimator != null) {
604631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // Cancel any offset animation
605631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.cancel();
606631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
607631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
608631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            return started;
609a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
610a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
611a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
613a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View target, int dx, int dy, int[] consumed) {
61450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            if (dy != 0 && !mSkipNestedPreScroll) {
61550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int min, max;
61650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                if (dy < 0) {
61750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // We're scrolling down
61850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    min = -child.getTotalScrollRange();
61950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    max = min + child.getDownNestedPreScrollRange();
62050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                } else {
62150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // We're scrolling up
62250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    min = -child.getUpNestedPreScrollRange();
62350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    max = 0;
62450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                }
62550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
626a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
627a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
628a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
629a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
630a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
631a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View target, int dxConsumed, int dyConsumed,
632a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int dxUnconsumed, int dyUnconsumed) {
63350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            if (dyUnconsumed < 0) {
63450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // If the scrolling view is scrolling down but not consuming, it's probably be at
63550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // the top of it's content
63650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                scroll(coordinatorLayout, child, dyUnconsumed,
63750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        -child.getDownNestedScrollRange(), 0);
63850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // Set the expanding flag so that onNestedPreScroll doesn't handle any events
63950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                mSkipNestedPreScroll = true;
64050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            } else {
64150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // As we're no longer handling nested scrolls, reset the skip flag
64250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                mSkipNestedPreScroll = false;
643a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
64450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
645a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
64650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Override
64750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
64850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                View target) {
64950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            // Reset the skip flag
65050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            mSkipNestedPreScroll = false;
65150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
65250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
65350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Override
654631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        public boolean onNestedFling(final CoordinatorLayout coordinatorLayout,
655631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                final AppBarLayout child, View target, float velocityX, float velocityY,
656631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                boolean consumed) {
657631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (!consumed) {
658631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // It has been consumed so let's fling ourselves
659631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                return fling(coordinatorLayout, child, -child.getTotalScrollRange(), 0, -velocityY);
660631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            } else {
661631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // If we're scrolling up and the child also consumed the fling. We'll fake scroll
662631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // upto our 'collapsed' offset
663631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                int targetScroll;
664631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                if (velocityY < 0) {
665631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // We're scrolling down
666631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    targetScroll = -child.getTotalScrollRange()
667631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                            + child.getDownNestedPreScrollRange();
668631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
669631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    if (getTopBottomOffsetForScrollingSibling() > targetScroll) {
670631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        // If we're currently expanded more than the target scroll, we'll return false
671631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        // now. This is so that we don't 'scroll' the wrong way.
672631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        return false;
673631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
674631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                } else {
675631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // We're scrolling up
676631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    targetScroll = -child.getUpNestedPreScrollRange();
677631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
678631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    if (getTopBottomOffsetForScrollingSibling() < targetScroll) {
679631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        // If we're currently expanded less than the target scroll, we'll return
680631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        // false now. This is so that we don't 'scroll' the wrong way.
681631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        return false;
682631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
683631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                }
684631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
685631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                if (mLogicalOffsetTop != targetScroll) {
686631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    animateOffsetTo(coordinatorLayout, child, targetScroll);
687631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    return true;
688631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                }
689631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
690631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
691631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            return false;
692631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
693631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
694631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private void animateOffsetTo(final CoordinatorLayout coordinatorLayout,
695631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                final AppBarLayout child, int offset) {
696631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (mAnimator == null) {
697631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator = ViewUtils.createAnimator();
698631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
699631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
700631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    @Override
701631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    public void onAnimationUpdate(ValueAnimatorCompat animator) {
702631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        setAppBarTopBottomOffset(coordinatorLayout, child,
703631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                                animator.getAnimatedIntValue());
704631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
705631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                });
706631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            } else {
707631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.cancel();
708631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
709631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
710631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            mAnimator.setIntValues(getTopBottomOffsetForScrollingSibling(), offset);
711631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            mAnimator.start();
712631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
713631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
714631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private boolean fling(CoordinatorLayout coordinatorLayout, AppBarLayout layout, int minOffset,
715631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                int maxOffset, float velocityY) {
716631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (mFlingRunnable != null) {
717631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                layout.removeCallbacks(mFlingRunnable);
718631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
719631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
720631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (mScroller == null) {
721631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mScroller = ScrollerCompat.create(layout.getContext());
722631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
723631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
724631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            mScroller.fling(
725631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    0, mLogicalOffsetTop, // curr
726631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    0, Math.round(velocityY), // velocity.
727631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    0, 0, // x
728631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    minOffset, maxOffset); // y
729631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
730631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (mScroller.computeScrollOffset()) {
731631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mFlingRunnable = new FlingRunnable(coordinatorLayout, layout);
732631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                ViewCompat.postOnAnimation(layout, mFlingRunnable);
733631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                return true;
734631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            } else {
735631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mFlingRunnable = null;
736631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                return false;
737631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
738631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
739631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
740631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private class FlingRunnable implements Runnable {
741631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            private final CoordinatorLayout mParent;
742631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            private final AppBarLayout mLayout;
743631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
744631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            FlingRunnable(CoordinatorLayout parent, AppBarLayout layout) {
745631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mParent = parent;
746631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mLayout = layout;
747631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
748631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
749631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            @Override
750631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            public void run() {
751631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                if (mLayout != null && mScroller != null && mScroller.computeScrollOffset()) {
752631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    setAppBarTopBottomOffset(mParent, mLayout, mScroller.getCurrY());
753631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
754631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // Post ourselves so that we run on the next animation
755631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    ViewCompat.postOnAnimation(mLayout, this);
756631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                }
757631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
758631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
759631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
760631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        @Override
76150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout child,
76250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int layoutDirection) {
76350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            boolean handled = super.onLayoutChild(parent, child, layoutDirection);
76450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
76550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            // Make sure we update the elevation
766631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            dispatchOffsetUpdates(child);
76750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
76850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            return handled;
769a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
770a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
771a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout,
77250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int dy, int minOffset, int maxOffset) {
773a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout,
774631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    mLogicalOffsetTop - dy, minOffset, maxOffset);
775a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
776a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
777631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout,
778631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                AppBarLayout appBarLayout, int newOffset) {
779631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, newOffset,
780631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    Integer.MIN_VALUE, Integer.MAX_VALUE);
781631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
782631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
783631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout,
78450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) {
785631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final int curOffset = mLogicalOffsetTop;
786a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            int consumed = 0;
787a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
788631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
789631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // If we have some scrolling range, and we're currently within the min and max
790631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // offsets, calculate a new offset
79150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset);
792a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
793a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (curOffset != newOffset) {
794a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    boolean offsetChanged = setTopAndBottomOffset(
795a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            appBarLayout.hasChildWithInterpolator()
796a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                                    ? interpolateOffset(appBarLayout, newOffset)
797a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                                    : newOffset);
798631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
799a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update how much dy we have consumed
800a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    consumed = curOffset - newOffset;
801a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update the stored sibling offset
802631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    mLogicalOffsetTop = newOffset;
803a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
804a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) {
805a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // If the offset hasn't changed and we're using an interpolated scroll
806a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // then we need to keep any dependent views updated. CoL will do this for
807a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // us when we move, but we need to do it manually when we don't (as an
808a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // interpolated scroll may finish early).
809a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        coordinatorLayout.dispatchDependentViewsChanged(appBarLayout);
810a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
811a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
812631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // Dispatch the updates to any listeners
813631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    dispatchOffsetUpdates(appBarLayout);
814a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
815a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
816a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
817a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return consumed;
818a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
819a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
820631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private void dispatchOffsetUpdates(AppBarLayout layout) {
821631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final List<WeakReference<OnOffsetChangedListener>> listeners = layout.mListeners;
82250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
823631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            // Iterate backwards through the list so that most recently added listeners
824631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            // get the first chance to decide
825631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            for (int i = 0, z = listeners.size(); i < z; i++) {
826631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                final WeakReference<OnOffsetChangedListener> ref = listeners.get(i);
827631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                final OnOffsetChangedListener listener = ref != null ? ref.get() : null;
82850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
829631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                if (listener != null) {
830631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    listener.onOffsetChanged(layout, getTopAndBottomOffset());
831a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
832a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
833a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
834a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
835a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int interpolateOffset(AppBarLayout layout, final int offset) {
836a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int absOffset = Math.abs(offset);
837a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
838a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = layout.getChildCount(); i < z; i++) {
839a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final View child = layout.getChildAt(i);
840a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
84150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                final Interpolator interpolator = childLp.getScrollInterpolator();
842a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
84350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                if (absOffset >= child.getTop() && absOffset <= child.getBottom()) {
844a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (interpolator != null) {
84550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        int childScrollableHeight = 0;
84650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        final int flags = childLp.getScrollFlags();
84750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
84850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            // We're set to scroll so add the child's height
84950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            childScrollableHeight += child.getHeight();
85050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
85150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                // For a collapsing scroll, we to take the collapsed height into account.
85250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                childScrollableHeight -= ViewCompat.getMinimumHeight(child);
85350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            }
85450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        }
85550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
85650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        if (childScrollableHeight > 0) {
85750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            final int offsetForView = absOffset - child.getTop();
85850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            final int interpolatedDiff = Math.round(childScrollableHeight *
85950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                    interpolator.getInterpolation(
86050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                            offsetForView / (float) childScrollableHeight));
86150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
86250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            return Integer.signum(offset) * (child.getTop() + interpolatedDiff);
86350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        }
864a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
86550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
86650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // If we get to here then the view on the offset isn't suitable for interpolated
86750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // scrolling. So break out of the loop
86850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    break;
869a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
870a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
871a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
872a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return offset;
873a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
874a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
875a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        final int getTopBottomOffsetForScrollingSibling() {
876631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            return mLogicalOffsetTop;
877a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
878a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
879a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
880a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
881a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Behavior which should be used by {@link View}s which can scroll vertically and support
882a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * nested scrolling to automatically scroll any {@link AppBarLayout} siblings.
883a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
884a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class ScrollingViewBehavior extends ViewOffsetBehavior<View> {
885a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int mOverlayTop;
886a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
887a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior() {}
888a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
889a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior(Context context, AttributeSet attrs) {
890a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
891a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
892a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            TypedArray a = context.obtainStyledAttributes(attrs,
893a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    R.styleable.ScrollingViewBehavior_Params);
894a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mOverlayTop = a.getDimensionPixelSize(
895a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0);
896a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
897a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
898a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
899a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
900a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
901a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // We depend on any AppBarLayouts
902a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return dependency instanceof AppBarLayout;
903a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
904a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
905a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
906a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onMeasureChild(CoordinatorLayout parent, View child,
907a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int parentWidthMeasureSpec, int widthUsed,
908a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int parentHeightMeasureSpec, int heightUsed) {
909a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (child.getLayoutParams().height == LayoutParams.MATCH_PARENT) {
910a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // If the child's height is set to match_parent then it with it's maximum visible
911a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // visible height
912a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
913a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final List<View> dependencies = parent.getDependencies(child);
914a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (dependencies.isEmpty()) {
915a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If we don't have any dependencies, return false
916a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return false;
917a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
918a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
919a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final AppBarLayout appBar = findFirstAppBarLayout(dependencies);
9206ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                if (appBar != null && ViewCompat.isLaidOut(appBar)) {
9216ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    if (ViewCompat.getFitsSystemWindows(appBar)) {
9226ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                        // If the AppBarLayout is fitting system windows then we need to also,
9236ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                        // otherwise we'll get CoL's compatible layout functionality
9246ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                        ViewCompat.setFitsSystemWindows(child, true);
925a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
926a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
927a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final int scrollRange = appBar.getTotalScrollRange();
9286ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes                    final int height = parent.getHeight()
929a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            - appBar.getMeasuredHeight() + scrollRange;
930a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
931a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            MeasureSpec.AT_MOST);
932a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
933a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Now measure the scrolling child with the correct height
934a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    parent.onMeasureChild(child, parentWidthMeasureSpec,
935a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            widthUsed, heightMeasureSpec, heightUsed);
936a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
937a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return true;
938a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
939a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
940a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return false;
941a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
942a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
943a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
944a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
945a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View dependency) {
946a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final CoordinatorLayout.Behavior behavior =
947a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
948a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (behavior instanceof Behavior) {
949a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // Offset the child so that it is below the app-bar (with any overlap)
950a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
951a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int appBarOffset = ((Behavior) behavior)
952a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        .getTopBottomOffsetForScrollingSibling();
953a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int expandedMax = dependency.getHeight() - mOverlayTop;
954a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int collapsedMin = parent.getHeight() - child.getHeight();
955a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
956a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (mOverlayTop != 0 && dependency instanceof AppBarLayout) {
957a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If we have an overlap top, and the dependency is an AppBarLayout, we control
958a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // the offset ourselves based on the appbar's scroll progress. This is so that
959a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // the scroll happens sequentially rather than linearly
960a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange();
961a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin,
962a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            Math.abs(appBarOffset) / (float) scrollRange));
963a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
964a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    setTopAndBottomOffset(MathUtils.constrain(
965a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            dependency.getHeight() - mOverlayTop + appBarOffset,
966a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            collapsedMin, expandedMax));
967a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
968a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
969a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return false;
970a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
971a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
972a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
973a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the distance that this view should overlap any {@link AppBarLayout}.
974a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
975a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param overlayTop the distance in px
976a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
977a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void setOverlayTop(int overlayTop) {
978a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mOverlayTop = overlayTop;
979a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
980a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
981a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
982a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the distance that this view should overlap any {@link AppBarLayout}.
983a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
984a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public int getOverlayTop() {
985a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mOverlayTop;
986a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
987a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
988a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private static AppBarLayout findFirstAppBarLayout(List<View> views) {
989a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = views.size(); i < z; i++) {
990a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View view = views.get(i);
991a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (view instanceof AppBarLayout) {
992a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return (AppBarLayout) view;
993a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
994a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
995a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return null;
996a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
997a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
998a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes}
999