AppBarLayout.java revision 631f64ec9c6255f38a7f746d7949b6a537c1180f
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;
24631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport android.support.v4.widget.ScrollerCompat;
25a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet;
26a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View;
27a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup;
28a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator;
29a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout;
30a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
3150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.Retention;
3250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.RetentionPolicy;
33631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.lang.ref.WeakReference;
34631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.ArrayList;
35631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.Iterator;
36a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List;
37a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
38a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/**
39a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of
40631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * material design's app bar concept, namely scrolling gestures.
41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through
43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute:
44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}.
45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}.
48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will
49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work.
50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to. The binding is done through
52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the {@link ScrollingViewBehavior} beahior class, meaning that you should set your scrolling
53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view's behavior to be an instance of {@link ScrollingViewBehavior}. A string resource containing
54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the full class name is available.
55a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
56a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre>
57a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;android.support.design.widget.CoordinatorLayout
58a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:android=&quot;http://schemas.android.com/apk/res/android";
59a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:app=&quot;http://schemas.android.com/apk/res-auto";
60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_width=&quot;match_parent&quot;
61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_height=&quot;match_parent&quot;&gt;
62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.v4.widget.NestedScrollView
64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;
65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;match_parent&quot;
66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             app:layout_behavior=&quot;@string/appbar_scrolling_view_behavior&quot;&gt;
67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;!-- Your scrolling content --&gt;
69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.v4.widget.NestedScrollView&gt;
71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.design.widget.AppBarLayout
73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;wrap_content&quot;
74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;&gt;
75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.v7.widget.Toolbar
77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.design.widget.TabLayout
81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.design.widget.AppBarLayout&gt;
85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;/android.support.design.widget.CoordinatorLayout&gt;
87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre>
88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar">
90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a>
91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */
92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout {
94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
96631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical
97631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * offset changes.
98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
99631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public interface OnOffsetChangedListener {
100a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows
102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * child views to implement custom behavior based on the offset (for instance pinning a
103a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * view at a certain y value).
104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
105631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes         * @param appBarLayout the {@link AppBarLayout} which offset has changed
10650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes         * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px
107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
108631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset);
109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private static final int INVALID_SCROLL_RANGE = -1;
112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mTotalScrollRange = INVALID_SCROLL_RANGE;
114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownPreScrollRange = INVALID_SCROLL_RANGE;
115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownScrollRange = INVALID_SCROLL_RANGE;
116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
117a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    boolean mHaveChildWithInterpolator;
118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
11950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    private float mTargetElevation;
12050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
121631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    private final List<WeakReference<OnOffsetChangedListener>> mListeners;
122631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context) {
124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        this(context, null);
125a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
126a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
127a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context, AttributeSet attrs) {
128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super(context, attrs);
129a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        setOrientation(VERTICAL);
13050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
13150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout,
13250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                0, R.style.Widget_Design_AppBarLayout);
13350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0);
13481520564f3dd783136e025174021ba4eabd6ff3cChris Banes        setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background));
13550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        a.recycle();
13650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
13750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        // Use the bounds view outline provider so that we cast a shadow, even without a background
13850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        ViewUtils.setBoundsViewOutlineProvider(this);
139631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
140631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        mListeners = new ArrayList<>();
141631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
142631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        ViewCompat.setElevation(this, mTargetElevation);
143631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
144631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
145631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
146631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Add a listener that will be called when the offset of this {@link AppBarLayout} changes.
147631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
148631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param listener The listener that will be called when the offset changes.]
149631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
150631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @see #removeOnOffsetChangedListener(OnOffsetChangedListener)
151631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
152631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void addOnOffsetChangedListener(OnOffsetChangedListener listener) {
153631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        for (int i = 0, z = mListeners.size(); i < z; i++) {
154631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final WeakReference<OnOffsetChangedListener> ref = mListeners.get(i);
155631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (ref != null && ref.get() == listener) {
156631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // Listener already added
157631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                return;
158631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
159631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
160631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        mListeners.add(new WeakReference<>(listener));
161631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
162631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
163631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
164631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Remove the previously added {@link OnOffsetChangedListener}.
165631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
166631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param listener the listener to remove.
167631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
168631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) {
169631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        final Iterator<WeakReference<OnOffsetChangedListener>> i = mListeners.iterator();
170631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        while (i.hasNext()) {
171631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final WeakReference<OnOffsetChangedListener> ref = i.next();
172631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final OnOffsetChangedListener item = ref.get();
173631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (item == listener || item == null) {
174631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // If the item is null, or is our given listener, remove
175631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                i.remove();
176631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
177631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
178a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
179a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
180a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
181a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected void onLayout(boolean changed, int l, int t, int r, int b) {
182a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.onLayout(changed, l, t, r, b);
183a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
184a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        // Invalidate the scroll ranges
185a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mTotalScrollRange = INVALID_SCROLL_RANGE;
186a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mDownPreScrollRange = INVALID_SCROLL_RANGE;
187a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mDownPreScrollRange = INVALID_SCROLL_RANGE;
188a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
189a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mHaveChildWithInterpolator = false;
190a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
191a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
192a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams childLp = (LayoutParams) child.getLayoutParams();
193a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final Interpolator interpolator = childLp.getScrollInterpolator();
194a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
195a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (interpolator != null) {
196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mHaveChildWithInterpolator = true;
197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
198a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
199a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
200a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
201a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public void setOrientation(int orientation) {
204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (orientation != VERTICAL) {
205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            throw new IllegalArgumentException("AppBarLayout is always vertical and does"
206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    + " not support horizontal orientation");
207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.setOrientation(orientation);
209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return p instanceof LayoutParams;
214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
216a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
217a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateDefaultLayoutParams() {
218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public LayoutParams generateLayoutParams(AttributeSet attrs) {
223a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(getContext(), attrs);
224a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (p instanceof LinearLayout.LayoutParams) {
229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((LinearLayout.LayoutParams) p);
230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        } else if (p instanceof MarginLayoutParams) {
231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((MarginLayoutParams) p);
232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(p);
234a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
235a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final boolean hasChildWithInterpolator() {
237a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mHaveChildWithInterpolator;
238a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
239a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
240a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
241a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range of any children
242a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     *
243a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * @return the scroll range in px
244a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
245a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getTotalScrollRange() {
246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mTotalScrollRange != INVALID_SCROLL_RANGE) {
247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mTotalScrollRange;
248a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
249a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
250a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
251a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
252a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
253a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
254a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int childHeight = ViewCompat.isLaidOut(child)
255a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ? child.getHeight()
256a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    : child.getMeasuredHeight();
257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
258a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
259a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
260a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                range += childHeight;
262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
264a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing scroll, we to take the collapsed height into account.
265a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // We also return the range straight away since later views can't scroll beneath
266a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // us
267a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return range - ViewCompat.getMinimumHeight(child);
268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
269a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
270a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mTotalScrollRange = range;
276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final boolean hasScrollableChildren() {
279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return getTotalScrollRange() != 0;
280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
283a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling up from a nested pre-scroll.
284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getUpNestedPreScrollRange() {
28650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        return getTotalScrollRange();
287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
290a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested pre-scroll.
291a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
292a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getDownNestedPreScrollRange() {
293a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownPreScrollRange != INVALID_SCROLL_RANGE) {
294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownPreScrollRange;
296a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = getChildCount() - 1; i >= 0; i--) {
300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
301a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int childHeight = ViewCompat.isLaidOut(child)
303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ? child.getHeight()
304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    : child.getMeasuredHeight();
305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) {
308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // The view has the quick return flag combination...
309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) {
310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If they're set to enter collapsed, use the minimum height
311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    range += ViewCompat.getMinimumHeight(child);
312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Else use the full height
314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    range += childHeight;
315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else if (range > 0) {
317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // If we've hit an non-quick return scrollable view, and we've already hit a
318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // quick return view, return now
319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mDownPreScrollRange = range;
323a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested scroll.
327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getDownNestedScrollRange() {
329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownScrollRange != INVALID_SCROLL_RANGE) {
330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownScrollRange;
332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = getChildCount() - 1; i >= 0; i--) {
336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int childHeight = ViewCompat.isLaidOut(child)
339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ? child.getHeight()
340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    : child.getMeasuredHeight();
341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                range += childHeight;
347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing exit scroll, we to take the collapsed height into account.
350a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // We also return the range straight away since later views can't scroll
351a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // beneath us
352a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return range - ViewCompat.getMinimumHeight(child);
353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mDownScrollRange = range;
361a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
36350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    final int getMinimumHeightForVisibleOverlappingContent() {
36450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        final int minHeight = ViewCompat.getMinimumHeight(this);
36550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        if (minHeight != 0) {
36650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            // If this layout has a min height, use it (doubled)
36750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            return minHeight * 2;
36850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
36950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
37050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        // Otherwise, we'll use twice the min height of our last child
37150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        final int childCount = getChildCount();
37250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        return childCount >= 1
37350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2
37450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                : 0;
37550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    }
37650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
37750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    /**
378631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Set the elevation value to use when this {@link AppBarLayout} should be elevated
379631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * above content.
380631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * <p>
381631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * This method does not do anything itself. A typical use for this method is called from within
382631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * an {@link OnOffsetChangedListener} when the offset has changed in such a way to require an
383631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * elevation change.
384631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
385631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @param elevation the elevation value to use.
386631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     *
387631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * @see ViewCompat#setElevation(View, float)
38850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes     */
389631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public void setTargetElevation(float elevation) {
390631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        mTargetElevation = elevation;
391631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    }
392631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
393631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    /**
394631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * Returns the elevation value to use when this {@link AppBarLayout} should be elevated
395631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     * above content.
396631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes     */
397631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes    public float getTargetElevation() {
39850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        return mTargetElevation;
39950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes    }
40050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class LayoutParams extends LinearLayout.LayoutParams {
40250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
40350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        /** @hide */
40450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @IntDef(flag=true, value={
40550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_SCROLL,
40650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_EXIT_UNTIL_COLLAPSED,
40750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_ENTER_ALWAYS,
40850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED
40950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        })
41050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Retention(RetentionPolicy.SOURCE)
41150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public @interface ScrollFlags {}
41250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * The view will be scroll in direct relation to scroll events. This flag needs to be
415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * set for any of the other flags to take effect. If any sibling views
416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * before this one do not have this flag, then this value has no effect.
417a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_SCROLL = 0x1;
419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
420a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
421a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When exiting (scrolling off screen) the view will be scrolled until it is
422a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * 'collapsed'. The collapsed height is defined by the view's minimum height.
423a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
424a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
425a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
426a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
427a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2;
428a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
429a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
430a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When entering (scrolling on screen) the view will scroll on any downwards
431a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * scroll event, regardless of whether the scrolling view is also scrolling. This
432a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * is commonly referred to as the 'quick return' pattern.
433a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
434a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4;
435a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
436a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
437a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * An additional flag for 'enterAlways' which modifies the returning view to
438a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * only initially scroll back to it's collapsed height. Once the scrolling view has
439a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * reached the end of it's scroll range, the remainder of this view will be scrolled
440a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * into view. The collapsed height is defined by the view's minimum height.
441a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
442a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
443a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
444a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8;
446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Internal flag which allows quick checking of 'quick return'
449a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS;
451a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
452a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int mScrollFlags = SCROLL_FLAG_SCROLL;
453a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        Interpolator mScrollInterpolator;
454a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
455a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(Context c, AttributeSet attrs) {
456a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(c, attrs);
457a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams);
458a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0);
459a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) {
460a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int resId = a.getResourceId(
461a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0);
462a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator(
463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        c, resId);
464a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
465a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
466a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
467a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
468a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height) {
469a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height);
470a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
471a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
472a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height, float weight) {
473a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height, weight);
474a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
475a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
476a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(ViewGroup.LayoutParams p) {
477a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(p);
478a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
479a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
480a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(MarginLayoutParams source) {
481a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
482a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
483a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
484a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LinearLayout.LayoutParams source) {
485a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
486a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
487a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
488a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LayoutParams source) {
489a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
490a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = source.mScrollFlags;
491a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = source.mScrollInterpolator;
492a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
493a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
494a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
495a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the scrolling flags.
496a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
497a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL},
498a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *             {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS}
499a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *             and {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED}.
500a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
501a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollFlags()
502a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
503a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags
504a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
50550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public void setScrollFlags(@ScrollFlags int flags) {
506a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = flags;
507a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
508a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the scrolling flags.
511a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
512a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollFlags(int)
513a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
514a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags
515a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
51650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @ScrollFlags
517a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public int getScrollFlags() {
518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollFlags;
519a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
520a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
521a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
522a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the interpolator to when scrolling the view associated with this
523a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}.
524a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
525a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling.
526a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
527a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator
528a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollInterpolator()
529a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
530a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void setScrollInterpolator(Interpolator interpolator) {
531a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = interpolator;
532a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
533a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
534a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
535a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the {@link Interpolator} being used for scrolling the view associated with this
536a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling.
537a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
538a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator
539a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollInterpolator(Interpolator)
540a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
541a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Interpolator getScrollInterpolator() {
542a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollInterpolator;
543a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
544a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
545a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
546a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
547a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested
548a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * scroll handling with offsetting.
549a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
550a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class Behavior extends ViewOffsetBehavior<AppBarLayout> {
551631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private int mLogicalOffsetTop;
552a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
55350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        private boolean mSkipNestedPreScroll;
554631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private Runnable mFlingRunnable;
555631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private ScrollerCompat mScroller;
556631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
557631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private ValueAnimatorCompat mAnimator;
55850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
559a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior() {}
560a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
561a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior(Context context, AttributeSet attrs) {
562a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
563a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
564a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
565a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
567a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View directTargetChild, View target, int nestedScrollAxes) {
568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // Return true if we're nested scrolling vertically and we have scrollable children
569631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0
570a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    && child.hasScrollableChildren();
571631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
572631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (started && mAnimator != null) {
573631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // Cancel any offset animation
574631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.cancel();
575631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
576631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
577631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            return started;
578a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
579a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
580a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
581a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
582a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View target, int dx, int dy, int[] consumed) {
58350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            if (dy != 0 && !mSkipNestedPreScroll) {
58450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int min, max;
58550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                if (dy < 0) {
58650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // We're scrolling down
58750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    min = -child.getTotalScrollRange();
58850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    max = min + child.getDownNestedPreScrollRange();
58950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                } else {
59050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // We're scrolling up
59150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    min = -child.getUpNestedPreScrollRange();
59250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    max = 0;
59350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                }
59450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
596a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
600a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View target, int dxConsumed, int dyConsumed,
601a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int dxUnconsumed, int dyUnconsumed) {
60250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            if (dyUnconsumed < 0) {
60350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // If the scrolling view is scrolling down but not consuming, it's probably be at
60450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // the top of it's content
60550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                scroll(coordinatorLayout, child, dyUnconsumed,
60650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        -child.getDownNestedScrollRange(), 0);
60750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // Set the expanding flag so that onNestedPreScroll doesn't handle any events
60850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                mSkipNestedPreScroll = true;
60950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            } else {
61050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                // As we're no longer handling nested scrolls, reset the skip flag
61150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                mSkipNestedPreScroll = false;
612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
61350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
614a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
61550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Override
61650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
61750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                View target) {
61850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            // Reset the skip flag
61950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            mSkipNestedPreScroll = false;
62050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        }
62150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
62250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        @Override
623631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        public boolean onNestedFling(final CoordinatorLayout coordinatorLayout,
624631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                final AppBarLayout child, View target, float velocityX, float velocityY,
625631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                boolean consumed) {
626631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (!consumed) {
627631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // It has been consumed so let's fling ourselves
628631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                return fling(coordinatorLayout, child, -child.getTotalScrollRange(), 0, -velocityY);
629631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            } else {
630631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // If we're scrolling up and the child also consumed the fling. We'll fake scroll
631631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // upto our 'collapsed' offset
632631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                int targetScroll;
633631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                if (velocityY < 0) {
634631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // We're scrolling down
635631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    targetScroll = -child.getTotalScrollRange()
636631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                            + child.getDownNestedPreScrollRange();
637631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
638631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    if (getTopBottomOffsetForScrollingSibling() > targetScroll) {
639631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        // If we're currently expanded more than the target scroll, we'll return false
640631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        // now. This is so that we don't 'scroll' the wrong way.
641631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        return false;
642631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
643631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                } else {
644631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // We're scrolling up
645631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    targetScroll = -child.getUpNestedPreScrollRange();
646631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
647631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    if (getTopBottomOffsetForScrollingSibling() < targetScroll) {
648631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        // If we're currently expanded less than the target scroll, we'll return
649631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        // false now. This is so that we don't 'scroll' the wrong way.
650631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        return false;
651631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
652631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                }
653631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
654631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                if (mLogicalOffsetTop != targetScroll) {
655631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    animateOffsetTo(coordinatorLayout, child, targetScroll);
656631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    return true;
657631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                }
658631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
659631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
660631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            return false;
661631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
662631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
663631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private void animateOffsetTo(final CoordinatorLayout coordinatorLayout,
664631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                final AppBarLayout child, int offset) {
665631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (mAnimator == null) {
666631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator = ViewUtils.createAnimator();
667631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
668631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
669631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    @Override
670631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    public void onAnimationUpdate(ValueAnimatorCompat animator) {
671631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                        setAppBarTopBottomOffset(coordinatorLayout, child,
672631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                                animator.getAnimatedIntValue());
673631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    }
674631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                });
675631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            } else {
676631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mAnimator.cancel();
677631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
678631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
679631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            mAnimator.setIntValues(getTopBottomOffsetForScrollingSibling(), offset);
680631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            mAnimator.start();
681631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
682631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
683631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private boolean fling(CoordinatorLayout coordinatorLayout, AppBarLayout layout, int minOffset,
684631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                int maxOffset, float velocityY) {
685631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (mFlingRunnable != null) {
686631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                layout.removeCallbacks(mFlingRunnable);
687631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
688631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
689631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (mScroller == null) {
690631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mScroller = ScrollerCompat.create(layout.getContext());
691631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
692631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
693631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            mScroller.fling(
694631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    0, mLogicalOffsetTop, // curr
695631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    0, Math.round(velocityY), // velocity.
696631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    0, 0, // x
697631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    minOffset, maxOffset); // y
698631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
699631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (mScroller.computeScrollOffset()) {
700631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mFlingRunnable = new FlingRunnable(coordinatorLayout, layout);
701631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                ViewCompat.postOnAnimation(layout, mFlingRunnable);
702631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                return true;
703631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            } else {
704631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mFlingRunnable = null;
705631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                return false;
706631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
707631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
708631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
709631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private class FlingRunnable implements Runnable {
710631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            private final CoordinatorLayout mParent;
711631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            private final AppBarLayout mLayout;
712631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
713631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            FlingRunnable(CoordinatorLayout parent, AppBarLayout layout) {
714631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mParent = parent;
715631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                mLayout = layout;
716631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
717631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
718631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            @Override
719631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            public void run() {
720631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                if (mLayout != null && mScroller != null && mScroller.computeScrollOffset()) {
721631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    setAppBarTopBottomOffset(mParent, mLayout, mScroller.getCurrY());
722631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
723631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // Post ourselves so that we run on the next animation
724631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    ViewCompat.postOnAnimation(mLayout, this);
725631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                }
726631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            }
727631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
728631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
729631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        @Override
73050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes        public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout child,
73150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int layoutDirection) {
73250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            boolean handled = super.onLayoutChild(parent, child, layoutDirection);
73350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
73450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            // Make sure we update the elevation
735631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            dispatchOffsetUpdates(child);
73650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
73750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes            return handled;
738a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
739a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
740a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout,
74150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                int dy, int minOffset, int maxOffset) {
742a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout,
743631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    mLogicalOffsetTop - dy, minOffset, maxOffset);
744a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
745a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
746631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout,
747631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                AppBarLayout appBarLayout, int newOffset) {
748631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, newOffset,
749631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    Integer.MIN_VALUE, Integer.MAX_VALUE);
750631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        }
751631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
752631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout,
75350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) {
754631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final int curOffset = mLogicalOffsetTop;
755a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            int consumed = 0;
756a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
757631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
758631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // If we have some scrolling range, and we're currently within the min and max
759631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                // offsets, calculate a new offset
76050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset);
761a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
762a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (curOffset != newOffset) {
763a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    boolean offsetChanged = setTopAndBottomOffset(
764a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            appBarLayout.hasChildWithInterpolator()
765a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                                    ? interpolateOffset(appBarLayout, newOffset)
766a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                                    : newOffset);
767631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes
768a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update how much dy we have consumed
769a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    consumed = curOffset - newOffset;
770a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update the stored sibling offset
771631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    mLogicalOffsetTop = newOffset;
772a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
773a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) {
774a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // If the offset hasn't changed and we're using an interpolated scroll
775a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // then we need to keep any dependent views updated. CoL will do this for
776a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // us when we move, but we need to do it manually when we don't (as an
777a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // interpolated scroll may finish early).
778a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        coordinatorLayout.dispatchDependentViewsChanged(appBarLayout);
779a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
780a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
781631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    // Dispatch the updates to any listeners
782631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    dispatchOffsetUpdates(appBarLayout);
783a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
784a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
785a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
786a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return consumed;
787a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
788a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
789631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes        private void dispatchOffsetUpdates(AppBarLayout layout) {
790631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            final List<WeakReference<OnOffsetChangedListener>> listeners = layout.mListeners;
79150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
792631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            // Iterate backwards through the list so that most recently added listeners
793631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            // get the first chance to decide
794631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            for (int i = 0, z = listeners.size(); i < z; i++) {
795631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                final WeakReference<OnOffsetChangedListener> ref = listeners.get(i);
796631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                final OnOffsetChangedListener listener = ref != null ? ref.get() : null;
79750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
798631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                if (listener != null) {
799631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes                    listener.onOffsetChanged(layout, getTopAndBottomOffset());
800a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
801a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
802a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
803a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
804a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int interpolateOffset(AppBarLayout layout, final int offset) {
805a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int absOffset = Math.abs(offset);
806a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
807a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = layout.getChildCount(); i < z; i++) {
808a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final View child = layout.getChildAt(i);
809a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
81050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                final Interpolator interpolator = childLp.getScrollInterpolator();
811a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
81250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                if (absOffset >= child.getTop() && absOffset <= child.getBottom()) {
813a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (interpolator != null) {
81450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        int childScrollableHeight = 0;
81550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        final int flags = childLp.getScrollFlags();
81650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
81750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            // We're set to scroll so add the child's height
81850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            childScrollableHeight += child.getHeight();
81950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
82050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                // For a collapsing scroll, we to take the collapsed height into account.
82150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                childScrollableHeight -= ViewCompat.getMinimumHeight(child);
82250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            }
82350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        }
82450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
82550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        if (childScrollableHeight > 0) {
82650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            final int offsetForView = absOffset - child.getTop();
82750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            final int interpolatedDiff = Math.round(childScrollableHeight *
82850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                    interpolator.getInterpolation(
82950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                                            offsetForView / (float) childScrollableHeight));
83050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
83150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                            return Integer.signum(offset) * (child.getTop() + interpolatedDiff);
83250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                        }
833a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
83450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes
83550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // If we get to here then the view on the offset isn't suitable for interpolated
83650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    // scrolling. So break out of the loop
83750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes                    break;
838a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
839a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
840a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
841a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return offset;
842a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
843a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
844a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        final int getTopBottomOffsetForScrollingSibling() {
845631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes            return mLogicalOffsetTop;
846a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
847a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
848a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
849a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
850a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Behavior which should be used by {@link View}s which can scroll vertically and support
851a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * nested scrolling to automatically scroll any {@link AppBarLayout} siblings.
852a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
853a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class ScrollingViewBehavior extends ViewOffsetBehavior<View> {
854a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int mOverlayTop;
855a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
856a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior() {}
857a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
858a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior(Context context, AttributeSet attrs) {
859a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
860a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
861a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            TypedArray a = context.obtainStyledAttributes(attrs,
862a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    R.styleable.ScrollingViewBehavior_Params);
863a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mOverlayTop = a.getDimensionPixelSize(
864a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0);
865a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
866a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
867a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
868a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
869a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
870a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // We depend on any AppBarLayouts
871a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return dependency instanceof AppBarLayout;
872a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
873a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
874a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
875a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onMeasureChild(CoordinatorLayout parent, View child,
876a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int parentWidthMeasureSpec, int widthUsed,
877a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int parentHeightMeasureSpec, int heightUsed) {
878a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (child.getLayoutParams().height == LayoutParams.MATCH_PARENT) {
879a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // If the child's height is set to match_parent then it with it's maximum visible
880a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // visible height
881a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
882a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final List<View> dependencies = parent.getDependencies(child);
883a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (dependencies.isEmpty()) {
884a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If we don't have any dependencies, return false
885a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return false;
886a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
887a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
888a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final AppBarLayout appBar = findFirstAppBarLayout(dependencies);
889a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (appBar != null) {
890a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (appBar.getMeasuredWidth() == 0 || appBar.getMeasuredHeight() == 0) {
891a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // If the AppBar hasn't been measured yet, we need to do it now
892a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        parent.onMeasureChild(appBar, parentWidthMeasureSpec,
893a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                                widthUsed, parentHeightMeasureSpec, heightUsed);
894a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
895a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
896a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final int scrollRange = appBar.getTotalScrollRange();
897a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final int height = MeasureSpec.getSize(parentHeightMeasureSpec)
898a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            - appBar.getMeasuredHeight() + scrollRange;
899a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
900a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            MeasureSpec.AT_MOST);
901a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
902a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Now measure the scrolling child with the correct height
903a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    parent.onMeasureChild(child, parentWidthMeasureSpec,
904a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            widthUsed, heightMeasureSpec, heightUsed);
905a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
906a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return true;
907a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
908a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
909a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return false;
910a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
911a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
912a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
913a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
914a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View dependency) {
915a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final CoordinatorLayout.Behavior behavior =
916a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
917a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (behavior instanceof Behavior) {
918a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // Offset the child so that it is below the app-bar (with any overlap)
919a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
920a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int appBarOffset = ((Behavior) behavior)
921a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        .getTopBottomOffsetForScrollingSibling();
922a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int expandedMax = dependency.getHeight() - mOverlayTop;
923a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int collapsedMin = parent.getHeight() - child.getHeight();
924a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
925a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (mOverlayTop != 0 && dependency instanceof AppBarLayout) {
926a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If we have an overlap top, and the dependency is an AppBarLayout, we control
927a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // the offset ourselves based on the appbar's scroll progress. This is so that
928a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // the scroll happens sequentially rather than linearly
929a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange();
930a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin,
931a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            Math.abs(appBarOffset) / (float) scrollRange));
932a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
933a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    setTopAndBottomOffset(MathUtils.constrain(
934a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            dependency.getHeight() - mOverlayTop + appBarOffset,
935a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            collapsedMin, expandedMax));
936a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
937a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
938a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return false;
939a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
940a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
941a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
942a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the distance that this view should overlap any {@link AppBarLayout}.
943a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
944a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param overlayTop the distance in px
945a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
946a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop
947a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
948a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void setOverlayTop(int overlayTop) {
949a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mOverlayTop = overlayTop;
950a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
951a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
952a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
953a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the distance that this view should overlap any {@link AppBarLayout}.
954a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
955a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop
956a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
957a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public int getOverlayTop() {
958a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mOverlayTop;
959a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
960a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
961a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private static AppBarLayout findFirstAppBarLayout(List<View> views) {
962a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = views.size(); i < z; i++) {
963a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View view = views.get(i);
964a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (view instanceof AppBarLayout) {
965a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return (AppBarLayout) view;
966a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
967a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
968a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return null;
969a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
970a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
971a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes}
972