AppBarLayout.java revision 6f730c0acfb10a929172ea2981a1aded0e39f5c7
1a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/* 2a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Copyright (C) 2015 The Android Open Source Project 3a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 4a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * you may not use this file except in compliance with the License. 6a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * You may obtain a copy of the License at 7a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 8a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * http://www.apache.org/licenses/LICENSE-2.0 9a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 10a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Unless required by applicable law or agreed to in writing, software 11a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * See the License for the specific language governing permissions and 14a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * limitations under the License. 15a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 16a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 17a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespackage android.support.design.widget; 18a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 19a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.Context; 20a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.res.TypedArray; 21e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcel; 22e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcelable; 2350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport android.support.annotation.IntDef; 24a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.design.R; 25a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat; 266ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banesimport android.support.v4.view.WindowInsetsCompat; 27631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport android.support.v4.widget.ScrollerCompat; 28a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet; 29a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View; 30a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup; 31a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator; 32a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout; 33a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 3450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.Retention; 3550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.RetentionPolicy; 36631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.ArrayList; 37a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List; 38a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 39a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/** 40a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of 419fb154338a62edc2c57dc036895199d6f1769400Chris Banes * material designs app bar concept, namely scrolling gestures. 42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through 44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute: 45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}. 46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}. 49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will 50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work. 51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 529fb154338a62edc2c57dc036895199d6f1769400Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to know when to scroll. 539fb154338a62edc2c57dc036895199d6f1769400Chris Banes * The binding is done through the {@link ScrollingViewBehavior} behavior class, meaning that you 549fb154338a62edc2c57dc036895199d6f1769400Chris Banes * should set your scrolling view's behavior to be an instance of {@link ScrollingViewBehavior}. 559fb154338a62edc2c57dc036895199d6f1769400Chris Banes * A string resource containing the full class name is available. 56a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 57a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre> 58a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.CoordinatorLayout 59a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:android="http://schemas.android.com/apk/res/android" 60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:app="http://schemas.android.com/apk/res-auto" 61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent"> 63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v4.widget.NestedScrollView 65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent" 67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_behavior="@string/appbar_scrolling_view_behavior"> 68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <!-- Your scrolling content --> 70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.v4.widget.NestedScrollView> 72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.AppBarLayout 74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="wrap_content" 75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent"> 76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v7.widget.Toolbar 78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.TabLayout 82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.AppBarLayout> 86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.CoordinatorLayout> 88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre> 89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar"> 91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a> 92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class) 94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout { 95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 969995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_NONE = 0x0; 979995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_EXPANDED = 0x1; 989995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_COLLAPSED = 0x2; 999995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4; 1009995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 102631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical 103631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * offset changes. 104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 105631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public interface OnOffsetChangedListener { 106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows 108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * child views to implement custom behavior based on the offset (for instance pinning a 109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view at a certain y value). 110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 111631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param appBarLayout the {@link AppBarLayout} which offset has changed 11250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px 113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 114631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset); 115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 117a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static final int INVALID_SCROLL_RANGE = -1; 118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 119a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mTotalScrollRange = INVALID_SCROLL_RANGE; 120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownPreScrollRange = INVALID_SCROLL_RANGE; 121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownScrollRange = INVALID_SCROLL_RANGE; 122a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes boolean mHaveChildWithInterpolator; 124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 12550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private float mTargetElevation; 12650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 1279995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private int mPendingAction = PENDING_ACTION_NONE; 1289995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 1296ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes private WindowInsetsCompat mLastInsets; 1306ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 1316f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes private final List<OnOffsetChangedListener> mListeners; 132631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 133a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context) { 134a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes this(context, null); 135a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 136a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 137a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context, AttributeSet attrs) { 138a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 139a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setOrientation(VERTICAL); 14050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 14150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout, 14250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 0, R.style.Widget_Design_AppBarLayout); 14350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0); 14481520564f3dd783136e025174021ba4eabd6ff3cChris Banes setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background)); 1459995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (a.hasValue(R.styleable.AppBarLayout_expanded)) { 1469995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false)); 1479995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 14850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes a.recycle(); 14950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 15050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Use the bounds view outline provider so that we cast a shadow, even without a background 15150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewUtils.setBoundsViewOutlineProvider(this); 152631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 153631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mListeners = new ArrayList<>(); 154631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 155631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.setElevation(this, mTargetElevation); 1566ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 1576ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes ViewCompat.setOnApplyWindowInsetsListener(this, 1586ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes new android.support.v4.view.OnApplyWindowInsetsListener() { 1596ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes @Override 1606ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes public WindowInsetsCompat onApplyWindowInsets(View v, 1616ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes WindowInsetsCompat insets) { 1626ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes setWindowInsets(insets); 1636ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes return insets.consumeSystemWindowInsets(); 1646ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 1656ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes }); 166631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 167631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 168631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 169631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Add a listener that will be called when the offset of this {@link AppBarLayout} changes. 170631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 171631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener The listener that will be called when the offset changes.] 172631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 173631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @see #removeOnOffsetChangedListener(OnOffsetChangedListener) 174631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 175631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void addOnOffsetChangedListener(OnOffsetChangedListener listener) { 1766f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes if (listener != null && !mListeners.contains(listener)) { 1776f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes mListeners.add(listener); 178631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 179631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 180631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 181631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 182631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Remove the previously added {@link OnOffsetChangedListener}. 183631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 184631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener the listener to remove. 185631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 186631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) { 1876f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes if (listener != null) { 1886f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes mListeners.remove(listener); 189631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 190a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 191a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 192a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 193a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected void onLayout(boolean changed, int l, int t, int r, int b) { 194a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.onLayout(changed, l, t, r, b); 195a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Invalidate the scroll ranges 197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 198a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 199a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 200a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 201a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = false; 202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = true; 209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOrientation(int orientation) { 216a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (orientation != VERTICAL) { 217a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes throw new IllegalArgumentException("AppBarLayout is always vertical and does" 218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes + " not support horizontal orientation"); 219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.setOrientation(orientation); 221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 2239995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes /** 2249995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * Sets whether this {@link AppBarLayout} is expanded or not, animating if it has already 2259995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * been laid out. 2269995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2279995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a 2289995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * direct child of a {@link CoordinatorLayout}.</p> 2299995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2309995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param expanded true if the layout should be fully expanded, false if it should 2319995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * be fully collapsed 2329995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2339995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_expanded 2349995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes */ 2359995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public void setExpanded(boolean expanded) { 2369995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes setExpanded(expanded, ViewCompat.isLaidOut(this)); 2379995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 2389995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 2399995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes /** 2409995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * Sets whether this {@link AppBarLayout} is expanded or not. 2419995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2429995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a 2439995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * direct child of a {@link CoordinatorLayout}.</p> 2449995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2459995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param expanded true if the layout should be fully expanded, false if it should 2469995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * be fully collapsed 2479995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param animate Whether to animate to the new state 2489995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2499995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_expanded 2509995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes */ 2519995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public void setExpanded(boolean expanded, boolean animate) { 2529995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mPendingAction = (expanded ? PENDING_ACTION_EXPANDED : PENDING_ACTION_COLLAPSED) 2539995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes | (animate ? PENDING_ACTION_ANIMATE_ENABLED : 0); 2549995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes requestLayout(); 2559995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 2569995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 258a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 259a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return p instanceof LayoutParams; 260a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateDefaultLayoutParams() { 264a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 265a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 266a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 267a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams generateLayoutParams(AttributeSet attrs) { 269a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(getContext(), attrs); 270a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (p instanceof LinearLayout.LayoutParams) { 275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((LinearLayout.LayoutParams) p); 276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (p instanceof MarginLayoutParams) { 277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((MarginLayoutParams) p); 278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(p); 280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasChildWithInterpolator() { 283a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mHaveChildWithInterpolator; 284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 286a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 2876ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes * Returns the scroll range of all children. 288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @return the scroll range in px 290a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 2916ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes public final int getTotalScrollRange() { 292a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mTotalScrollRange != INVALID_SCROLL_RANGE) { 293a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange; 294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 296a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 301a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 307b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes range += childHeight + lp.topMargin + lp.bottomMargin; 308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 3116ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // We also break straight away since later views can't scroll beneath 312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us 3136ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes range -= ViewCompat.getMinimumHeight(child); 3146ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes break; 315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 3226ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes final int top = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; 3236ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes return mTotalScrollRange = (range - top); 324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasScrollableChildren() { 327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return getTotalScrollRange() != 0; 328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling up from a nested pre-scroll. 332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getUpNestedPreScrollRange() { 33450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return getTotalScrollRange(); 335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested pre-scroll. 339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedPreScrollRange() { 341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownPreScrollRange != INVALID_SCROLL_RANGE) { 342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange; 344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 350a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 351a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 352a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) { 356b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // First take the margin into account 357b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes range += lp.topMargin + lp.bottomMargin; 358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // The view has the quick return flag combination... 359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) { 360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If they're set to enter collapsed, use the minimum height 361a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += ViewCompat.getMinimumHeight(child); 362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Else use the full height 364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 365a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (range > 0) { 367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we've hit an non-quick return scrollable view, and we've already hit a 368a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // quick return view, return now 369a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 370a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 371a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 372a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange = range; 373a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested scroll. 377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 378a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedScrollRange() { 379a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownScrollRange != INVALID_SCROLL_RANGE) { 380a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange; 382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 384a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 385c1ce4f6cf4d99d6856c4259bf1bb1cb56f604737Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 386a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 387a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 388b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes int childHeight = ViewCompat.isLaidOut(child) 389a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 390a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 391b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes childHeight += lp.topMargin + lp.bottomMargin; 392a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 393a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 394a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 397a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 398a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 399a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 400a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing exit scroll, we to take the collapsed height into account. 401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We also return the range straight away since later views can't scroll 402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // beneath us 403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return range - ViewCompat.getMinimumHeight(child); 404a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 405a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 406a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 407a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange = range; 412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 41450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int getMinimumHeightForVisibleOverlappingContent() { 4156ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes final int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; 41650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int minHeight = ViewCompat.getMinimumHeight(this); 41750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (minHeight != 0) { 41850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If this layout has a min height, use it (doubled) 4196ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes return (minHeight * 2) + topInset; 42050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 42150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 42250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Otherwise, we'll use twice the min height of our last child 42350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childCount = getChildCount(); 42450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return childCount >= 1 4256ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes ? (ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2) + topInset 42650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes : 0; 42750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 42850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 42950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** 430631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Set the elevation value to use when this {@link AppBarLayout} should be elevated 431631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * above content. 432631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * <p> 433631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * This method does not do anything itself. A typical use for this method is called from within 434631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * an {@link OnOffsetChangedListener} when the offset has changed in such a way to require an 435631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * elevation change. 436631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 437631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param elevation the elevation value to use. 438631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 439631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @see ViewCompat#setElevation(View, float) 44050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes */ 441631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void setTargetElevation(float elevation) { 442631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mTargetElevation = elevation; 443631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 444631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 445631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 446631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Returns the elevation value to use when this {@link AppBarLayout} should be elevated 447631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * above content. 448631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 449631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public float getTargetElevation() { 45050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return mTargetElevation; 45150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 45250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 4539995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes int getPendingAction() { 4549995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes return mPendingAction; 4559995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 4569995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 4579995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes void resetPendingAction() { 4589995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mPendingAction = PENDING_ACTION_NONE; 4599995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 4609995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 4616ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes private void setWindowInsets(WindowInsetsCompat insets) { 4626ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // Invalidate the total scroll range... 4636ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 4646ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes mLastInsets = insets; 4656ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 4666ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // Now dispatch them to our children 4676ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 4686ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes final View child = getChildAt(i); 4696ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes insets = ViewCompat.dispatchApplyWindowInsets(child, insets); 4706ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes if (insets.isConsumed()) { 4716ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes break; 4726ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 4736ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 4746ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 4756ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 476a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class LayoutParams extends LinearLayout.LayoutParams { 47750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 47850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** @hide */ 47950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @IntDef(flag=true, value={ 48050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_SCROLL, 48150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_EXIT_UNTIL_COLLAPSED, 48250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS, 48350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED 48450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes }) 48550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Retention(RetentionPolicy.SOURCE) 48650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public @interface ScrollFlags {} 48750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 488a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 489a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The view will be scroll in direct relation to scroll events. This flag needs to be 490a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * set for any of the other flags to take effect. If any sibling views 491a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * before this one do not have this flag, then this value has no effect. 492a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 493a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_SCROLL = 0x1; 494a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 495a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 496a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When exiting (scrolling off screen) the view will be scrolled until it is 497a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 'collapsed'. The collapsed height is defined by the view's minimum height. 498a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 499a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 500a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 501a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 502a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2; 503a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 504a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 505a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When entering (scrolling on screen) the view will scroll on any downwards 506a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll event, regardless of whether the scrolling view is also scrolling. This 507a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * is commonly referred to as the 'quick return' pattern. 508a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4; 510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 511a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 512a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * An additional flag for 'enterAlways' which modifies the returning view to 513a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * only initially scroll back to it's collapsed height. Once the scrolling view has 514a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * reached the end of it's scroll range, the remainder of this view will be scrolled 515a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * into view. The collapsed height is defined by the view's minimum height. 516a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 517a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 519a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 520a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8; 521a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 522a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 523a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Internal flag which allows quick checking of 'quick return' 524a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 525a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS; 526a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 527a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int mScrollFlags = SCROLL_FLAG_SCROLL; 528a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Interpolator mScrollInterpolator; 529a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 530a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(Context c, AttributeSet attrs) { 531a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(c, attrs); 532a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams); 533a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0); 534a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) { 535a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int resId = a.getResourceId( 536a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0); 537a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator( 538a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes c, resId); 539a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 540a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 541a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 542a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 543a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height) { 544a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height); 545a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 546a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 547a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height, float weight) { 548a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height, weight); 549a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 550a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 551a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(ViewGroup.LayoutParams p) { 552a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(p); 553a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 554a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 555a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(MarginLayoutParams source) { 556a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 557a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 558a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 559a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LinearLayout.LayoutParams source) { 560a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 561a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 562a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 563a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LayoutParams source) { 564a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 565a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = source.mScrollFlags; 566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = source.mScrollInterpolator; 567a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 569a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 570a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the scrolling flags. 571a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 572a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL}, 573a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS} 574a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * and {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED}. 575a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 576a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollFlags() 577a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 578149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags 579a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 58050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void setScrollFlags(@ScrollFlags int flags) { 581a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = flags; 582a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 583a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 584a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 585a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the scrolling flags. 586a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 587a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollFlags(int) 588a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 589149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags 590a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 59150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @ScrollFlags 592a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getScrollFlags() { 593a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollFlags; 594a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 596a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the interpolator to when scrolling the view associated with this 598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. 599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 600a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling. 601a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 602149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator 603a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollInterpolator() 604a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 605a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setScrollInterpolator(Interpolator interpolator) { 606a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = interpolator; 607a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 608a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 609a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 610a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the {@link Interpolator} being used for scrolling the view associated with this 611a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling. 612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 613149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator 614a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollInterpolator(Interpolator) 615a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 616a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Interpolator getScrollInterpolator() { 617a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollInterpolator; 618a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 619a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 620a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 621a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 622a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested 623a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll handling with offsetting. 624a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 625a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class Behavior extends ViewOffsetBehavior<AppBarLayout> { 626e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private static final int INVALID_POSITION = -1; 627e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 628e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private int mOffsetDelta; 629a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 63050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private boolean mSkipNestedPreScroll; 631631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private Runnable mFlingRunnable; 632631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private ScrollerCompat mScroller; 633631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 634631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private ValueAnimatorCompat mAnimator; 63550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 636e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private int mOffsetToChildIndexOnLayout = INVALID_POSITION; 637e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private boolean mOffsetToChildIndexOnLayoutIsMinHeight; 638e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private float mOffsetToChildIndexOnLayoutPerc; 639e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 640a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior() {} 641a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 642a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior(Context context, AttributeSet attrs) { 643a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 644a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 645a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 646a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 647a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 648a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View directTargetChild, View target, int nestedScrollAxes) { 649f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes // Return true if we're nested scrolling vertically, and we have scrollable children 650f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes // and the scrolling view is big enough to scroll 651631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 652f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes && child.hasScrollableChildren() 653f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes && coordinatorLayout.getHeight() - target.getHeight() <= child.getHeight(); 654631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 655631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (started && mAnimator != null) { 656631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Cancel any offset animation 657631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.cancel(); 658631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 659631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 660631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return started; 661a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 662a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 663a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 664a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 665a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dx, int dy, int[] consumed) { 66650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy != 0 && !mSkipNestedPreScroll) { 66750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int min, max; 66850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy < 0) { 66950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling down 67050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getTotalScrollRange(); 67150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = min + child.getDownNestedPreScrollRange(); 67250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 67350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling up 67450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getUpNestedPreScrollRange(); 67550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = 0; 67650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 67750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes consumed[1] = scroll(coordinatorLayout, child, dy, min, max); 678a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 679a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 680a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 681a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 682a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 683a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dxConsumed, int dyConsumed, 684a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int dxUnconsumed, int dyUnconsumed) { 68550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dyUnconsumed < 0) { 68650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If the scrolling view is scrolling down but not consuming, it's probably be at 68750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // the top of it's content 68850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes scroll(coordinatorLayout, child, dyUnconsumed, 68950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes -child.getDownNestedScrollRange(), 0); 69050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Set the expanding flag so that onNestedPreScroll doesn't handle any events 69150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = true; 69250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 69350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // As we're no longer handling nested scrolls, reset the skip flag 69450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 695a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 69650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 697a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 69850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 69950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 70050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes View target) { 70150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Reset the skip flag 70250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 70350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 70450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 70550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 706631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public boolean onNestedFling(final CoordinatorLayout coordinatorLayout, 707631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final AppBarLayout child, View target, float velocityX, float velocityY, 708631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes boolean consumed) { 709631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (!consumed) { 710631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // It has been consumed so let's fling ourselves 711631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return fling(coordinatorLayout, child, -child.getTotalScrollRange(), 0, -velocityY); 712631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 713631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're scrolling up and the child also consumed the fling. We'll fake scroll 714631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // upto our 'collapsed' offset 715631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes int targetScroll; 716631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (velocityY < 0) { 717631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling down 718631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes targetScroll = -child.getTotalScrollRange() 719631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes + child.getDownNestedPreScrollRange(); 720631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 721631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (getTopBottomOffsetForScrollingSibling() > targetScroll) { 722631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're currently expanded more than the target scroll, we'll return false 723631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // now. This is so that we don't 'scroll' the wrong way. 724631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 725631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 726631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 727631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling up 728631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes targetScroll = -child.getUpNestedPreScrollRange(); 729631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 730631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (getTopBottomOffsetForScrollingSibling() < targetScroll) { 731631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're currently expanded less than the target scroll, we'll return 732631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // false now. This is so that we don't 'scroll' the wrong way. 733631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 734631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 735631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 736631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 737e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (getTopBottomOffsetForScrollingSibling() != targetScroll) { 738631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes animateOffsetTo(coordinatorLayout, child, targetScroll); 739631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return true; 740631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 741631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 742631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 743631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 744631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 745631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 746631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private void animateOffsetTo(final CoordinatorLayout coordinatorLayout, 747631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final AppBarLayout child, int offset) { 748631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mAnimator == null) { 749631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator = ViewUtils.createAnimator(); 750631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR); 751631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() { 752631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 753631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void onAnimationUpdate(ValueAnimatorCompat animator) { 754631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes setAppBarTopBottomOffset(coordinatorLayout, child, 755631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes animator.getAnimatedIntValue()); 756631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 757631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes }); 758631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 759631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.cancel(); 760631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 761631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 762631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setIntValues(getTopBottomOffsetForScrollingSibling(), offset); 763631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.start(); 764631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 765631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 766631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private boolean fling(CoordinatorLayout coordinatorLayout, AppBarLayout layout, int minOffset, 767631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes int maxOffset, float velocityY) { 768631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mFlingRunnable != null) { 769631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes layout.removeCallbacks(mFlingRunnable); 770631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 771631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 772631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mScroller == null) { 773631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mScroller = ScrollerCompat.create(layout.getContext()); 774631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 775631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 776631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mScroller.fling( 777e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 0, getTopBottomOffsetForScrollingSibling(), // curr 778631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 0, Math.round(velocityY), // velocity. 779631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 0, 0, // x 780631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes minOffset, maxOffset); // y 781631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 782631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mScroller.computeScrollOffset()) { 783631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mFlingRunnable = new FlingRunnable(coordinatorLayout, layout); 784631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.postOnAnimation(layout, mFlingRunnable); 785631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return true; 786631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 787631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mFlingRunnable = null; 788631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 789631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 790631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 791631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 792631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private class FlingRunnable implements Runnable { 793631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private final CoordinatorLayout mParent; 794631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private final AppBarLayout mLayout; 795631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 796631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes FlingRunnable(CoordinatorLayout parent, AppBarLayout layout) { 797631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mParent = parent; 798631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mLayout = layout; 799631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 800631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 801631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 802631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void run() { 803631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mLayout != null && mScroller != null && mScroller.computeScrollOffset()) { 804631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes setAppBarTopBottomOffset(mParent, mLayout, mScroller.getCurrY()); 805631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 806631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Post ourselves so that we run on the next animation 807631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.postOnAnimation(mLayout, this); 808631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 809631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 810631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 811631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 812631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 8139995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, 81450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int layoutDirection) { 8159995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes boolean handled = super.onLayoutChild(parent, abl, layoutDirection); 8169995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 8179995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final int pendingAction = abl.getPendingAction(); 8189995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (pendingAction != PENDING_ACTION_NONE) { 8199995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final boolean animate = (pendingAction & PENDING_ACTION_ANIMATE_ENABLED) != 0; 8209995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if ((pendingAction & PENDING_ACTION_COLLAPSED) != 0) { 8219995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final int offset = -abl.getUpNestedPreScrollRange(); 8229995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (animate) { 8239995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes animateOffsetTo(parent, abl, offset); 8249995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else { 8259995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes setAppBarTopBottomOffset(parent, abl, offset); 8269995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 8279995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else if ((pendingAction & PENDING_ACTION_EXPANDED) != 0) { 8289995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (animate) { 8299995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes animateOffsetTo(parent, abl, 0); 8309995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else { 8319995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes setAppBarTopBottomOffset(parent, abl, 0); 8329995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 8339995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 8349995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes // Finally reset the pending state 8359995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes abl.resetPendingAction(); 8369995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else if (mOffsetToChildIndexOnLayout >= 0) { 8379995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes View child = abl.getChildAt(mOffsetToChildIndexOnLayout); 838e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes int offset = -child.getBottom(); 839e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (mOffsetToChildIndexOnLayoutIsMinHeight) { 840e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes offset += ViewCompat.getMinimumHeight(child); 841e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } else { 842e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc); 843e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 844e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes setTopAndBottomOffset(offset); 845e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = INVALID_POSITION; 846e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 84750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 84850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Make sure we update the elevation 8499995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes dispatchOffsetUpdates(abl); 85050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 85150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return handled; 852a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 853a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 854a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout, 85550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int dy, int minOffset, int maxOffset) { 856a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, 857e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes getTopBottomOffsetForScrollingSibling() - dy, minOffset, maxOffset); 858a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 859a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 860631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout, 861631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes AppBarLayout appBarLayout, int newOffset) { 862631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, newOffset, 863631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes Integer.MIN_VALUE, Integer.MAX_VALUE); 864631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 865631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 866631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout, 86750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) { 868e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int curOffset = getTopBottomOffsetForScrollingSibling(); 869a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int consumed = 0; 870a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 871631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) { 872631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we have some scrolling range, and we're currently within the min and max 873631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // offsets, calculate a new offset 87450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset); 875a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 876a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (curOffset != newOffset) { 877e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int interpolatedOffset = appBarLayout.hasChildWithInterpolator() 878e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ? interpolateOffset(appBarLayout, newOffset) 879e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes : newOffset; 880e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 881e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset); 882631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 883a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update how much dy we have consumed 884a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes consumed = curOffset - newOffset; 885a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update the stored sibling offset 886e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetDelta = newOffset - interpolatedOffset; 887a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 888a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) { 889a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the offset hasn't changed and we're using an interpolated scroll 890a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // then we need to keep any dependent views updated. CoL will do this for 891a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us when we move, but we need to do it manually when we don't (as an 892a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // interpolated scroll may finish early). 893a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes coordinatorLayout.dispatchDependentViewsChanged(appBarLayout); 894a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 895a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 896631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Dispatch the updates to any listeners 897631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes dispatchOffsetUpdates(appBarLayout); 898a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 899a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 900a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 901a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return consumed; 902a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 903a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 904631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private void dispatchOffsetUpdates(AppBarLayout layout) { 9056f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes final List<OnOffsetChangedListener> listeners = layout.mListeners; 90650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 907631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Iterate backwards through the list so that most recently added listeners 908631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // get the first chance to decide 909631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes for (int i = 0, z = listeners.size(); i < z; i++) { 9106f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes final OnOffsetChangedListener listener = listeners.get(i); 911631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (listener != null) { 912631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes listener.onOffsetChanged(layout, getTopAndBottomOffset()); 913a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 914a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 915a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 916a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 917a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int interpolateOffset(AppBarLayout layout, final int offset) { 918a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int absOffset = Math.abs(offset); 919a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 920a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 921a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = layout.getChildAt(i); 922a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 92350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 924a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 92550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (absOffset >= child.getTop() && absOffset <= child.getBottom()) { 926a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 92750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int childScrollableHeight = 0; 92850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int flags = childLp.getScrollFlags(); 92950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 930b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // We're set to scroll so add the child's height plus margin 931b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes childScrollableHeight += child.getHeight() + childLp.topMargin 932b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes + childLp.bottomMargin; 933b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes 93450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 935b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // For a collapsing scroll, we to take the collapsed height 936b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // into account. 93750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight -= ViewCompat.getMinimumHeight(child); 93850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 93950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 94050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 94150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childScrollableHeight > 0) { 94250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int offsetForView = absOffset - child.getTop(); 94350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int interpolatedDiff = Math.round(childScrollableHeight * 94450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes interpolator.getInterpolation( 94550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes offsetForView / (float) childScrollableHeight)); 94650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 94750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return Integer.signum(offset) * (child.getTop() + interpolatedDiff); 94850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 949a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 95050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 95150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If we get to here then the view on the offset isn't suitable for interpolated 95250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // scrolling. So break out of the loop 95350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes break; 954a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 955a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 956a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 957a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return offset; 958a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 959a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 960a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getTopBottomOffsetForScrollingSibling() { 961e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return getTopAndBottomOffset() + mOffsetDelta; 962e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 963e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 964e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 965e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout) { 966e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final Parcelable superState = super.onSaveInstanceState(parent, appBarLayout); 967e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int offset = getTopAndBottomOffset(); 968e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 969e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes // Try and find the first visible child... 970e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes for (int i = 0, count = appBarLayout.getChildCount(); i < count; i++) { 971e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes View child = appBarLayout.getChildAt(i); 972e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int visBottom = child.getBottom() + offset; 973e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 974e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (child.getTop() + offset <= 0 && visBottom >= 0) { 975e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final SavedState ss = new SavedState(superState); 976e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibleChildIndex = i; 977e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibileChildAtMinimumHeight = 978e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes visBottom == ViewCompat.getMinimumHeight(child); 979e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibileChildPercentageShown = visBottom / (float) child.getHeight(); 980e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return ss; 981e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 982e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 983e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 984e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes // Else we'll just return the super state 985e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return superState; 986e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 987e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 988e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 989e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout, 990e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes Parcelable state) { 991e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (state instanceof SavedState) { 992e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final SavedState ss = (SavedState) state; 993e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.onRestoreInstanceState(parent, appBarLayout, ss.getSuperState()); 994e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = ss.firstVisibleChildIndex; 995e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayoutPerc = ss.firstVisibileChildPercentageShown; 996e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayoutIsMinHeight = ss.firstVisibileChildAtMinimumHeight; 997e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } else { 998e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.onRestoreInstanceState(parent, appBarLayout, state); 999e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = INVALID_POSITION; 1000e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1001e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1002e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1003e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes protected static class SavedState extends View.BaseSavedState { 1004e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes int firstVisibleChildIndex; 1005e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes float firstVisibileChildPercentageShown; 1006e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes boolean firstVisibileChildAtMinimumHeight; 1007e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1008e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState(Parcel source) { 1009e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super(source); 1010e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibleChildIndex = source.readInt(); 1011e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibileChildPercentageShown = source.readFloat(); 1012e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibileChildAtMinimumHeight = source.readByte() != 0; 1013e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1014e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1015e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState(Parcelable superState) { 1016e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super(superState); 1017e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1018e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1019e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1020e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public void writeToParcel(Parcel dest, int flags) { 1021e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.writeToParcel(dest, flags); 1022e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeInt(firstVisibleChildIndex); 1023e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeFloat(firstVisibileChildPercentageShown); 1024e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeByte((byte) (firstVisibileChildAtMinimumHeight ? 1 : 0)); 1025e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1026e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1027e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public static final Parcelable.Creator<SavedState> CREATOR = 1028e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes new Parcelable.Creator<SavedState>() { 1029e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1030e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState createFromParcel(Parcel source) { 1031e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return new SavedState(source); 1032e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1033e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1034e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1035e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState[] newArray(int size) { 1036e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return new SavedState[size]; 1037e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1038e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes }; 1039a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1040a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1041a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1042a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 1043a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Behavior which should be used by {@link View}s which can scroll vertically and support 1044a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * nested scrolling to automatically scroll any {@link AppBarLayout} siblings. 1045a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 1046a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class ScrollingViewBehavior extends ViewOffsetBehavior<View> { 1047a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mOverlayTop; 1048a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1049a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior() {} 1050a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1051a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior(Context context, AttributeSet attrs) { 1052a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 1053a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1054a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, 1055a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params); 1056a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = a.getDimensionPixelSize( 1057a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0); 1058a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 1059a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1060a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1061a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1062a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { 1063a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We depend on any AppBarLayouts 1064a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return dependency instanceof AppBarLayout; 1065a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1066a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1067a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1068a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onMeasureChild(CoordinatorLayout parent, View child, 1069a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentWidthMeasureSpec, int widthUsed, 1070a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentHeightMeasureSpec, int heightUsed) { 1071a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (child.getLayoutParams().height == LayoutParams.MATCH_PARENT) { 1072f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes // If the child's height is set to match_parent then measure it with it's 1073f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes // maximum visible visible height 1074a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1075a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final List<View> dependencies = parent.getDependencies(child); 1076a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dependencies.isEmpty()) { 1077a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we don't have any dependencies, return false 1078a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 1079a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1080a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1081a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout appBar = findFirstAppBarLayout(dependencies); 10826ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes if (appBar != null && ViewCompat.isLaidOut(appBar)) { 10836ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes if (ViewCompat.getFitsSystemWindows(appBar)) { 10846ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // If the AppBarLayout is fitting system windows then we need to also, 10856ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // otherwise we'll get CoL's compatible layout functionality 10866ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes ViewCompat.setFitsSystemWindows(child, true); 1087a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1088a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 108955f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes int availableHeight = MeasureSpec.getSize(parentHeightMeasureSpec); 109055f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes if (availableHeight == 0) { 109155f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes // If the measure spec doesn't specify a size, use the current height 109255f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes availableHeight = parent.getHeight(); 109355f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes } 109455f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes final int height = availableHeight - appBar.getMeasuredHeight() 1095f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes + appBar.getTotalScrollRange(); 1096a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 1097a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes MeasureSpec.AT_MOST); 1098a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1099a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Now measure the scrolling child with the correct height 1100a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes parent.onMeasureChild(child, parentWidthMeasureSpec, 1101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes widthUsed, heightMeasureSpec, heightUsed); 1102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1103a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return true; 1104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1105a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 1107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onDependentViewChanged(CoordinatorLayout parent, View child, 1111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View dependency) { 1112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final CoordinatorLayout.Behavior behavior = 1113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); 1114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (behavior instanceof Behavior) { 1115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Offset the child so that it is below the app-bar (with any overlap) 1116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1117a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int appBarOffset = ((Behavior) behavior) 1118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes .getTopBottomOffsetForScrollingSibling(); 1119a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int expandedMax = dependency.getHeight() - mOverlayTop; 1120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int collapsedMin = parent.getHeight() - child.getHeight(); 1121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1122a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mOverlayTop != 0 && dependency instanceof AppBarLayout) { 1123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we have an overlap top, and the dependency is an AppBarLayout, we control 1124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the offset ourselves based on the appbar's scroll progress. This is so that 1125a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the scroll happens sequentially rather than linearly 1126a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange(); 1127a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin, 1128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Math.abs(appBarOffset) / (float) scrollRange)); 1129a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 1130f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes setTopAndBottomOffset(dependency.getHeight() - mOverlayTop + appBarOffset); 1131a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1132a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1133a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 1134a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1135a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1136a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 1137a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the distance that this view should overlap any {@link AppBarLayout}. 1138a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 1139a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param overlayTop the distance in px 1140a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 1141a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOverlayTop(int overlayTop) { 1142a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = overlayTop; 1143a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1144a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1145a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 1146a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the distance that this view should overlap any {@link AppBarLayout}. 1147a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 1148a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getOverlayTop() { 1149a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mOverlayTop; 1150a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1151a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1152a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static AppBarLayout findFirstAppBarLayout(List<View> views) { 1153a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = views.size(); i < z; i++) { 1154a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View view = views.get(i); 1155a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (view instanceof AppBarLayout) { 1156a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return (AppBarLayout) view; 1157a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1158a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1159a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return null; 1160a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1161a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1162a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes} 1163