AppBarLayout.java revision a6a508b2296730ca6954aaebcca52a9962a5cb55
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;
21a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.design.R;
22a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat;
23a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet;
24a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View;
25a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup;
26a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator;
27a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout;
28a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
29a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.lang.ref.WeakReference;
30a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List;
31a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
32a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/**
33a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of
34a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Material Design's App bar concept, namely scrolling gestures.
35a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
36a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through
37a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute:
38a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}.
39a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
40a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}.
42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will
43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work.
44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to. The binding is done through
46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the {@link ScrollingViewBehavior} beahior class, meaning that you should set your scrolling
47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view's behavior to be an instance of {@link ScrollingViewBehavior}. A string resource containing
48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the full class name is available.
49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre>
51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;android.support.design.widget.CoordinatorLayout
52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:android=&quot;http://schemas.android.com/apk/res/android";
53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         xmlns:app=&quot;http://schemas.android.com/apk/res-auto";
54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_width=&quot;match_parent&quot;
55a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         android:layout_height=&quot;match_parent&quot;&gt;
56a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
57a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.v4.widget.NestedScrollView
58a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;
59a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;match_parent&quot;
60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             app:layout_behavior=&quot;@string/appbar_scrolling_view_behavior&quot;&gt;
61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;!-- Your scrolling content --&gt;
63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.v4.widget.NestedScrollView&gt;
65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;android.support.design.widget.AppBarLayout
67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_height=&quot;wrap_content&quot;
68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *             android:layout_width=&quot;match_parent&quot;&gt;
69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.v7.widget.Toolbar
71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *         &lt;android.support.design.widget.TabLayout
75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 ...
76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *                 app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     &lt;/android.support.design.widget.AppBarLayout&gt;
79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * &lt;/android.support.design.widget.CoordinatorLayout&gt;
81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre>
82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar">
84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *     http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a>
85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */
86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout {
88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Interface which allows an implementing child {@link View} of this {@link AppBarLayout} to
91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * receive offset updates, and provide extra information.
92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public interface AppBarLayoutChild {
94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows
96a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * child views to implement custom behavior based on the offset (for instance pinning a
97a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * view at a certain y value).
98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
99a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param leftRightOffset the left and right offset, in px
100a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param topBottomOffset the top and bottom offset, in px
101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        void onOffsetUpdate(int leftRightOffset, int topBottomOffset);
103a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
105a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private static final int INVALID_SCROLL_RANGE = -1;
106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mTotalScrollRange = INVALID_SCROLL_RANGE;
108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownPreScrollRange = INVALID_SCROLL_RANGE;
109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mDownScrollRange = INVALID_SCROLL_RANGE;
110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    boolean mHaveChildWithInterpolator;
112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context) {
114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        this(context, null);
115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
117a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public AppBarLayout(Context context, AttributeSet attrs) {
118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super(context, attrs);
119a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        setOrientation(VERTICAL);
120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
122a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected void onLayout(boolean changed, int l, int t, int r, int b) {
124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.onLayout(changed, l, t, r, b);
125a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
126a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        // Invalidate the scroll ranges
127a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mTotalScrollRange = INVALID_SCROLL_RANGE;
128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mDownPreScrollRange = INVALID_SCROLL_RANGE;
129a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mDownPreScrollRange = INVALID_SCROLL_RANGE;
130a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
131a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mHaveChildWithInterpolator = false;
132a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
133a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
134a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams childLp = (LayoutParams) child.getLayoutParams();
135a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final Interpolator interpolator = childLp.getScrollInterpolator();
136a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
137a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (interpolator != null) {
138a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mHaveChildWithInterpolator = true;
139a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
140a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
141a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
142a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
143a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
144a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
145a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public void setOrientation(int orientation) {
146a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (orientation != VERTICAL) {
147a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            throw new IllegalArgumentException("AppBarLayout is always vertical and does"
148a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    + " not support horizontal orientation");
149a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
150a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        super.setOrientation(orientation);
151a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
152a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
153a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
154a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
155a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return p instanceof LayoutParams;
156a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
157a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
158a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
159a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateDefaultLayoutParams() {
160a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
161a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
162a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
163a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
164a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public LayoutParams generateLayoutParams(AttributeSet attrs) {
165a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(getContext(), attrs);
166a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
167a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
168a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    @Override
169a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
170a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (p instanceof LinearLayout.LayoutParams) {
171a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((LinearLayout.LayoutParams) p);
172a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        } else if (p instanceof MarginLayoutParams) {
173a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return new LayoutParams((MarginLayoutParams) p);
174a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
175a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return new LayoutParams(p);
176a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
177a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
178a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final boolean hasChildWithInterpolator() {
179a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mHaveChildWithInterpolator;
180a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
181a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
182a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
183a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range of any children
184a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     *
185a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * @return the scroll range in px
186a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
187a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getTotalScrollRange() {
188a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mTotalScrollRange != INVALID_SCROLL_RANGE) {
189a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mTotalScrollRange;
190a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
191a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
192a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
193a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = 0, z = getChildCount(); i < z; i++) {
194a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
195a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int childHeight = ViewCompat.isLaidOut(child)
197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ? child.getHeight()
198a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    : child.getMeasuredHeight();
199a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
200a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
201a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                range += childHeight;
204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing scroll, we to take the collapsed height into account.
207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // We also return the range straight away since later views can't scroll beneath
208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // us
209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return range - ViewCompat.getMinimumHeight(child);
210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
216a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
217a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mTotalScrollRange = range;
218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final boolean hasScrollableChildren() {
221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return getTotalScrollRange() != 0;
222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
223a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
224a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling up from a nested pre-scroll.
226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getUpNestedPreScrollRange() {
228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return getTotalScrollRange() - getUpNestedScrollRange();
229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling up from a nested scroll.
233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * @return
234a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
235a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getUpNestedScrollRange() {
236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return 0;
237a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
238a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
239a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
240a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested pre-scroll.
241a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
242a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getDownNestedPreScrollRange() {
243a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownPreScrollRange != INVALID_SCROLL_RANGE) {
244a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
245a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownPreScrollRange;
246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
248a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int range = 0;
249a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        for (int i = getChildCount() - 1; i >= 0; i--) {
250a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final View child = getChildAt(i);
251a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
252a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int childHeight = ViewCompat.isLaidOut(child)
253a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ? child.getHeight()
254a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    : child.getMeasuredHeight();
255a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
256a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) {
258a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // The view has the quick return flag combination...
259a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) {
260a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If they're set to enter collapsed, use the minimum height
261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    range += ViewCompat.getMinimumHeight(child);
262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Else use the full height
264a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    range += childHeight;
265a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
266a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else if (range > 0) {
267a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // If we've hit an non-quick return scrollable view, and we've already hit a
268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // quick return view, return now
269a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
270a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mDownPreScrollRange = range;
273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getMinimumHeightForVisibleOverlappingContent() {
276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        final int minHeight = ViewCompat.getMinimumHeight(this);
277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (minHeight != 0) {
278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If this layout has a min height, use it (doubled)
279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return minHeight * 2;
280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        // Otherwise, we'll use twice the min height of our last child
283a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        final int childCount = getChildCount();
284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return childCount >= 1
285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2
286a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                : 0;
287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
290a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Return the scroll range when scrolling down from a nested scroll.
291a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
292a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    final int getDownNestedScrollRange() {
293a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mDownScrollRange != INVALID_SCROLL_RANGE) {
294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // If we already have a valid value, return it
295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mDownScrollRange;
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
306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int flags = lp.mScrollFlags;
307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // We're set to scroll so add the child's height
310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                range += childHeight;
311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // For a collapsing exit scroll, we to take the collapsed height into account.
314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // We also return the range straight away since later views can't scroll
315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // beneath us
316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return range - ViewCompat.getMinimumHeight(child);
317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else {
319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // As soon as a view doesn't have the scroll flag, we end the range calculation.
320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // This is because views below can not scroll under a fixed view.
321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                break;
322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
323a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mDownScrollRange = range;
325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class LayoutParams extends LinearLayout.LayoutParams {
328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * The view will be scroll in direct relation to scroll events. This flag needs to be
330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * set for any of the other flags to take effect. If any sibling views
331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * before this one do not have this flag, then this value has no effect.
332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_SCROLL = 0x1;
334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When exiting (scrolling off screen) the view will be scrolled until it is
337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * 'collapsed'. The collapsed height is defined by the view's minimum height.
338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2;
343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * When entering (scrolling on screen) the view will scroll on any downwards
346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * scroll event, regardless of whether the scrolling view is also scrolling. This
347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * is commonly referred to as the 'quick return' pattern.
348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4;
350a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
351a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
352a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * An additional flag for 'enterAlways' which modifies the returning view to
353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * only initially scroll back to it's collapsed height. Once the scrolling view has
354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * reached the end of it's scroll range, the remainder of this view will be scrolled
355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * into view. The collapsed height is defined by the view's minimum height.
356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see ViewCompat#getMinimumHeight(View)
358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see View#setMinimumHeight(int)
359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8;
361a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Internal flag which allows quick checking of 'quick return'
364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
365a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS;
366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        int mScrollFlags = SCROLL_FLAG_SCROLL;
368a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        Interpolator mScrollInterpolator;
369a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
370a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(Context c, AttributeSet attrs) {
371a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(c, attrs);
372a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams);
373a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0);
374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) {
375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int resId = a.getResourceId(
376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0);
377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator(
378a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        c, resId);
379a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
380a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height) {
384a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height);
385a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
386a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
387a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(int width, int height, float weight) {
388a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(width, height, weight);
389a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
390a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
391a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(ViewGroup.LayoutParams p) {
392a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(p);
393a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
394a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(MarginLayoutParams source) {
396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
397a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
398a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
399a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LinearLayout.LayoutParams source) {
400a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public LayoutParams(LayoutParams source) {
404a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(source);
405a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = source.mScrollFlags;
406a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = source.mScrollInterpolator;
407a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the scrolling flags.
411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL},
413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *             {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS}
414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *             and {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED}.
415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollFlags()
417a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags
419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
420a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void setScrollFlags(int flags) {
421a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollFlags = flags;
422a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
423a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
424a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
425a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the scrolling flags.
426a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
427a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollFlags(int)
428a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
429a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags
430a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
431a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public int getScrollFlags() {
432a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollFlags;
433a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
434a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
435a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
436a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the interpolator to when scrolling the view associated with this
437a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}.
438a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
439a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling.
440a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
441a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator
442a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #getScrollInterpolator()
443a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
444a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void setScrollInterpolator(Interpolator interpolator) {
445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mScrollInterpolator = interpolator;
446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
449a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the {@link Interpolator} being used for scrolling the view associated with this
450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling.
451a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
452a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator
453a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @see #setScrollInterpolator(Interpolator)
454a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
455a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Interpolator getScrollInterpolator() {
456a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mScrollInterpolator;
457a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
458a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
459a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
460a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
461a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested
462a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * scroll handling with offsetting.
463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
464a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class Behavior extends ViewOffsetBehavior<AppBarLayout> {
465a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int mSiblingOffsetTop;
466a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
467a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior() {}
468a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
469a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public Behavior(Context context, AttributeSet attrs) {
470a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
471a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
472a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
473a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
474a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
475a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View directTargetChild, View target, int nestedScrollAxes) {
476a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // Return true if we're nested scrolling vertically and we have scrollable children
477a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0
478a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    && child.hasScrollableChildren();
479a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
480a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
481a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
482a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
483a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View target, int dx, int dy, int[] consumed) {
484a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (dy != 0) {
485a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                consumed[1] = scroll(coordinatorLayout, child, dy, dy < 0
486a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        ? -child.getDownNestedPreScrollRange()
487a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        : -child.getUpNestedPreScrollRange());
488a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
489a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
490a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
491a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
492a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
493a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View target, int dxConsumed, int dyConsumed,
494a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int dxUnconsumed, int dyUnconsumed) {
495a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            int dy = 0;
496a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (dyConsumed > 0) {
497a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // If the scrolling view is scrolling up, we offset
498a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                dy = dyConsumed;
499a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else if (dyUnconsumed < 0) {
500a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // If the scrolling view is scrolling down, we offset anything it doesn't consume
501a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                dy = dyUnconsumed;
502a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
503a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
504a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (dy != 0) {
505a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                scroll(coordinatorLayout, child, dy, dy < 0
506a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        ? -child.getDownNestedScrollRange()
507a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        : -child.getUpNestedScrollRange());
508a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
511a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout,
512a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int dy, int minOffset) {
513a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout,
514a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    mSiblingOffsetTop - dy,
515a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    minOffset);
516a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
517a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout,
519a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                AppBarLayout appBarLayout, int newOffset, final int minOffset) {
520a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int curOffset = mSiblingOffsetTop;
521a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            int consumed = 0;
522a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
523a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (curOffset >= minOffset) {
524a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                newOffset = MathUtils.constrain(newOffset, minOffset, 0);
525a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
526a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (curOffset != newOffset) {
527a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    boolean offsetChanged = setTopAndBottomOffset(
528a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            appBarLayout.hasChildWithInterpolator()
529a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                                    ? interpolateOffset(appBarLayout, newOffset)
530a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                                    : newOffset);
531a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update how much dy we have consumed
532a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    consumed = curOffset - newOffset;
533a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Update the stored sibling offset
534a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    mSiblingOffsetTop = newOffset;
535a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
536a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) {
537a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // If the offset hasn't changed and we're using an interpolated scroll
538a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // then we need to keep any dependent views updated. CoL will do this for
539a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // us when we move, but we need to do it manually when we don't (as an
540a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // interpolated scroll may finish early).
541a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        coordinatorLayout.dispatchDependentViewsChanged(appBarLayout);
542a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
543a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
544a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    dispatchOffsetUpdates(appBarLayout);
545a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
546a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
547a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
548a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return consumed;
549a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
550a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
551a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private void dispatchOffsetUpdates(AppBarLayout layout) {
552a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = layout.getChildCount(); i < z; i++) {
553a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View child = layout.getChildAt(i);
554a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (child instanceof AppBarLayoutChild) {
555a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ((AppBarLayoutChild) child).onOffsetUpdate(
556a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            getLeftAndRightOffset(), getTopAndBottomOffset());
557a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
558a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
559a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
560a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
561a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int interpolateOffset(AppBarLayout layout, final int offset) {
562a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            int heightSoFar = 0;
563a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final int absOffset = Math.abs(offset);
564a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
565a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = layout.getChildCount(); i < z; i++) {
566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final View child = layout.getChildAt(i);
567a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int scrollFlags = childLp.getScrollFlags();
569a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int previousHeightSoFar = heightSoFar;
570a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int childHeight = child.getHeight();
571a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
572a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int childScrollableHeight = 0;
573a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if ((scrollFlags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
574a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // We're set to scroll so add the child's height
575a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    childScrollableHeight += child.getHeight();
576a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if ((scrollFlags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
577a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // For a collapsing scroll, we to take the collapsed height into account.
578a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        childScrollableHeight -= ViewCompat.getMinimumHeight(child);
579a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
580a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
581a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
582a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // Now update the height encountered
583a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                heightSoFar += childHeight;
584a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
585a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (absOffset >= previousHeightSoFar && absOffset <= heightSoFar) {
586a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final Interpolator interpolator = childLp.getScrollInterpolator();
587a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
588a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (interpolator != null) {
589a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        final int offsetForView = absOffset - previousHeightSoFar;
590a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        final int interpolatedDiff = Math.round(childScrollableHeight *
591a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                                interpolator.getInterpolation(
592a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                                        offsetForView / (float) childScrollableHeight));
593a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
594a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        return Integer.signum(offset) * (previousHeightSoFar + interpolatedDiff);
595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    } else {
596a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        break;
597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
600a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
601a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return offset;
602a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
603a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
604a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        final int getTopBottomOffsetForScrollingSibling() {
605a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mSiblingOffsetTop;
606a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
607a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
608a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
609a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
610a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Behavior which should be used by {@link View}s which can scroll vertically and support
611a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * nested scrolling to automatically scroll any {@link AppBarLayout} siblings.
612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
613a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public static class ScrollingViewBehavior extends ViewOffsetBehavior<View> {
614a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private int mOverlayTop;
615a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
616a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior() {}
617a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
618a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public ScrollingViewBehavior(Context context, AttributeSet attrs) {
619a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            super(context, attrs);
620a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
621a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            TypedArray a = context.obtainStyledAttributes(attrs,
622a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    R.styleable.ScrollingViewBehavior_Params);
623a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mOverlayTop = a.getDimensionPixelSize(
624a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0);
625a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            a.recycle();
626a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
627a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
628a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
629a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
630a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // We depend on any AppBarLayouts
631a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return dependency instanceof AppBarLayout;
632a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
633a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
634a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
635a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onMeasureChild(CoordinatorLayout parent, View child,
636a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int parentWidthMeasureSpec, int widthUsed,
637a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                int parentHeightMeasureSpec, int heightUsed) {
638a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (child.getLayoutParams().height == LayoutParams.MATCH_PARENT) {
639a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // If the child's height is set to match_parent then it with it's maximum visible
640a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // visible height
641a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
642a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final List<View> dependencies = parent.getDependencies(child);
643a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (dependencies.isEmpty()) {
644a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If we don't have any dependencies, return false
645a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return false;
646a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
647a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
648a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final AppBarLayout appBar = findFirstAppBarLayout(dependencies);
649a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (appBar != null) {
650a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    if (appBar.getMeasuredWidth() == 0 || appBar.getMeasuredHeight() == 0) {
651a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        // If the AppBar hasn't been measured yet, we need to do it now
652a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        parent.onMeasureChild(appBar, parentWidthMeasureSpec,
653a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                                widthUsed, parentHeightMeasureSpec, heightUsed);
654a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    }
655a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
656a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final int scrollRange = appBar.getTotalScrollRange();
657a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final int height = MeasureSpec.getSize(parentHeightMeasureSpec)
658a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            - appBar.getMeasuredHeight() + scrollRange;
659a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
660a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            MeasureSpec.AT_MOST);
661a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
662a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // Now measure the scrolling child with the correct height
663a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    parent.onMeasureChild(child, parentWidthMeasureSpec,
664a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            widthUsed, heightMeasureSpec, heightUsed);
665a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
666a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return true;
667a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
668a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
669a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return false;
670a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
671a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
672a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        @Override
673a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
674a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View dependency) {
675a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final CoordinatorLayout.Behavior behavior =
676a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
677a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (behavior instanceof Behavior) {
678a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                // Offset the child so that it is below the app-bar (with any overlap)
679a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
680a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int appBarOffset = ((Behavior) behavior)
681a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                        .getTopBottomOffsetForScrollingSibling();
682a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int expandedMax = dependency.getHeight() - mOverlayTop;
683a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final int collapsedMin = parent.getHeight() - child.getHeight();
684a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
685a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (mOverlayTop != 0 && dependency instanceof AppBarLayout) {
686a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // If we have an overlap top, and the dependency is an AppBarLayout, we control
687a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // the offset ourselves based on the appbar's scroll progress. This is so that
688a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    // the scroll happens sequentially rather than linearly
689a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange();
690a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin,
691a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            Math.abs(appBarOffset) / (float) scrollRange));
692a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                } else {
693a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    setTopAndBottomOffset(MathUtils.constrain(
694a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            dependency.getHeight() - mOverlayTop + appBarOffset,
695a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            collapsedMin, expandedMax));
696a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
697a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
698a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return false;
699a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
700a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
701a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
702a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Set the distance that this view should overlap any {@link AppBarLayout}.
703a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
704a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @param overlayTop the distance in px
705a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
706a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop
707a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
708a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public void setOverlayTop(int overlayTop) {
709a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mOverlayTop = overlayTop;
710a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
711a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
712a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        /**
713a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * Returns the distance that this view should overlap any {@link AppBarLayout}.
714a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         *
715a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop
716a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes         */
717a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        public int getOverlayTop() {
718a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return mOverlayTop;
719a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
720a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
721a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private static AppBarLayout findFirstAppBarLayout(List<View> views) {
722a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = views.size(); i < z; i++) {
723a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View view = views.get(i);
724a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (view instanceof AppBarLayout) {
725a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    return (AppBarLayout) view;
726a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                }
727a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
728a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return null;
729a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
730a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
731a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes}
732