AppBarLayout.java revision c1ce4f6cf4d99d6856c4259bf1bb1cb56f604737
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.lang.ref.WeakReference; 37631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.ArrayList; 38631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.Iterator; 39a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List; 40a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/** 42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of 439fb154338a62edc2c57dc036895199d6f1769400Chris Banes * material designs app bar concept, namely scrolling gestures. 44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through 46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute: 47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}. 48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}. 51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will 52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work. 53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 549fb154338a62edc2c57dc036895199d6f1769400Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to know when to scroll. 559fb154338a62edc2c57dc036895199d6f1769400Chris Banes * The binding is done through the {@link ScrollingViewBehavior} behavior class, meaning that you 569fb154338a62edc2c57dc036895199d6f1769400Chris Banes * should set your scrolling view's behavior to be an instance of {@link ScrollingViewBehavior}. 579fb154338a62edc2c57dc036895199d6f1769400Chris Banes * A string resource containing the full class name is available. 58a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 59a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre> 60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.CoordinatorLayout 61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:android="http://schemas.android.com/apk/res/android" 62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:app="http://schemas.android.com/apk/res-auto" 63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent"> 65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v4.widget.NestedScrollView 67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent" 69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_behavior="@string/appbar_scrolling_view_behavior"> 70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <!-- Your scrolling content --> 72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.v4.widget.NestedScrollView> 74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.AppBarLayout 76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="wrap_content" 77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent"> 78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v7.widget.Toolbar 80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.TabLayout 84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.AppBarLayout> 88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.CoordinatorLayout> 90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre> 91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar"> 93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a> 94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class) 96a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout { 97a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 99631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical 100631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * offset changes. 101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 102631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public interface OnOffsetChangedListener { 103a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows 105a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * child views to implement custom behavior based on the offset (for instance pinning a 106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view at a certain y value). 107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 108631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param appBarLayout the {@link AppBarLayout} which offset has changed 10950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px 110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 111631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset); 112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static final int INVALID_SCROLL_RANGE = -1; 115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mTotalScrollRange = INVALID_SCROLL_RANGE; 117a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownPreScrollRange = INVALID_SCROLL_RANGE; 118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownScrollRange = INVALID_SCROLL_RANGE; 119a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes boolean mHaveChildWithInterpolator; 121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 12250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private float mTargetElevation; 12350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 1246ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes private WindowInsetsCompat mLastInsets; 1256ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 126631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private final List<WeakReference<OnOffsetChangedListener>> mListeners; 127631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context) { 129a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes this(context, null); 130a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 131a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 132a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context, AttributeSet attrs) { 133a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 134a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setOrientation(VERTICAL); 13550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 13650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout, 13750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 0, R.style.Widget_Design_AppBarLayout); 13850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0); 13981520564f3dd783136e025174021ba4eabd6ff3cChris Banes setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background)); 14050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes a.recycle(); 14150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 14250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Use the bounds view outline provider so that we cast a shadow, even without a background 14350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewUtils.setBoundsViewOutlineProvider(this); 144631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 145631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mListeners = new ArrayList<>(); 146631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 147631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.setElevation(this, mTargetElevation); 1486ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 1496ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes ViewCompat.setOnApplyWindowInsetsListener(this, 1506ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes new android.support.v4.view.OnApplyWindowInsetsListener() { 1516ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes @Override 1526ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes public WindowInsetsCompat onApplyWindowInsets(View v, 1536ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes WindowInsetsCompat insets) { 1546ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes setWindowInsets(insets); 1556ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes return insets.consumeSystemWindowInsets(); 1566ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 1576ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes }); 158631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 159631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 160631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 161631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Add a listener that will be called when the offset of this {@link AppBarLayout} changes. 162631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 163631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener The listener that will be called when the offset changes.] 164631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 165631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @see #removeOnOffsetChangedListener(OnOffsetChangedListener) 166631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 167631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void addOnOffsetChangedListener(OnOffsetChangedListener listener) { 168631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes for (int i = 0, z = mListeners.size(); i < z; i++) { 169631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final WeakReference<OnOffsetChangedListener> ref = mListeners.get(i); 170631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (ref != null && ref.get() == listener) { 171631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Listener already added 172631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return; 173631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 174631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 175631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mListeners.add(new WeakReference<>(listener)); 176631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 177631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 178631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 179631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Remove the previously added {@link OnOffsetChangedListener}. 180631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 181631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener the listener to remove. 182631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 183631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) { 184631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final Iterator<WeakReference<OnOffsetChangedListener>> i = mListeners.iterator(); 185631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes while (i.hasNext()) { 186631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final WeakReference<OnOffsetChangedListener> ref = i.next(); 187631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final OnOffsetChangedListener item = ref.get(); 188631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (item == listener || item == null) { 189631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If the item is null, or is our given listener, remove 190631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes i.remove(); 191631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 192631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 193a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 194a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 195a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected void onLayout(boolean changed, int l, int t, int r, int b) { 197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.onLayout(changed, l, t, r, b); 198a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 199a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Invalidate the scroll ranges 200a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 201a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = false; 205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = true; 212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 216a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 217a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOrientation(int orientation) { 219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (orientation != VERTICAL) { 220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes throw new IllegalArgumentException("AppBarLayout is always vertical and does" 221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes + " not support horizontal orientation"); 222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 223a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.setOrientation(orientation); 224a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return p instanceof LayoutParams; 229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateDefaultLayoutParams() { 233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 234a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 235a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 237a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams generateLayoutParams(AttributeSet attrs) { 238a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(getContext(), attrs); 239a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 240a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 241a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 242a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 243a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (p instanceof LinearLayout.LayoutParams) { 244a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((LinearLayout.LayoutParams) p); 245a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (p instanceof MarginLayoutParams) { 246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((MarginLayoutParams) p); 247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 248a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(p); 249a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 250a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 251a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasChildWithInterpolator() { 252a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mHaveChildWithInterpolator; 253a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 254a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 255a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 2566ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes * Returns the scroll range of all children. 257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 258a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @return the scroll range in px 259a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 2606ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes public final int getTotalScrollRange() { 261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mTotalScrollRange != INVALID_SCROLL_RANGE) { 262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange; 263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 264a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 265a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 266a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 267a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 269a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 270a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 2806ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // We also break straight away since later views can't scroll beneath 281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us 2826ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes range -= ViewCompat.getMinimumHeight(child); 2836ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes break; 284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 286a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 290a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 2916ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes final int top = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; 2926ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes return mTotalScrollRange = (range - top); 293a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasScrollableChildren() { 296a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return getTotalScrollRange() != 0; 297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling up from a nested pre-scroll. 301a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getUpNestedPreScrollRange() { 30350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return getTotalScrollRange(); 304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested pre-scroll. 308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedPreScrollRange() { 310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownPreScrollRange != INVALID_SCROLL_RANGE) { 311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange; 313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 323a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) { 325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // The view has the quick return flag combination... 326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) { 327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If they're set to enter collapsed, use the minimum height 328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += ViewCompat.getMinimumHeight(child); 329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Else use the full height 331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (range > 0) { 334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we've hit an non-quick return scrollable view, and we've already hit a 335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // quick return view, return now 336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange = range; 340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested scroll. 344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedScrollRange() { 346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownScrollRange != INVALID_SCROLL_RANGE) { 347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange; 349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 350a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 351a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 352c1ce4f6cf4d99d6856c4259bf1bb1cb56f604737Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 361a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 365a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing exit scroll, we to take the collapsed height into account. 367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We also return the range straight away since later views can't scroll 368a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // beneath us 369a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return range - ViewCompat.getMinimumHeight(child); 370a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 371a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 372a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 373a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange = range; 378a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 379a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 38050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int getMinimumHeightForVisibleOverlappingContent() { 3816ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes final int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; 38250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int minHeight = ViewCompat.getMinimumHeight(this); 38350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (minHeight != 0) { 38450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If this layout has a min height, use it (doubled) 3856ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes return (minHeight * 2) + topInset; 38650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 38750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 38850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Otherwise, we'll use twice the min height of our last child 38950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childCount = getChildCount(); 39050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return childCount >= 1 3916ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes ? (ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2) + topInset 39250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes : 0; 39350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 39450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 39550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** 396631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Set the elevation value to use when this {@link AppBarLayout} should be elevated 397631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * above content. 398631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * <p> 399631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * This method does not do anything itself. A typical use for this method is called from within 400631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * an {@link OnOffsetChangedListener} when the offset has changed in such a way to require an 401631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * elevation change. 402631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 403631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param elevation the elevation value to use. 404631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 405631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @see ViewCompat#setElevation(View, float) 40650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes */ 407631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void setTargetElevation(float elevation) { 408631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mTargetElevation = elevation; 409631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 410631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 411631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 412631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Returns the elevation value to use when this {@link AppBarLayout} should be elevated 413631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * above content. 414631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 415631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public float getTargetElevation() { 41650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return mTargetElevation; 41750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 41850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 4196ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes private void setWindowInsets(WindowInsetsCompat insets) { 4206ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // Invalidate the total scroll range... 4216ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 4226ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes mLastInsets = insets; 4236ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 4246ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // Now dispatch them to our children 4256ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 4266ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes final View child = getChildAt(i); 4276ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes insets = ViewCompat.dispatchApplyWindowInsets(child, insets); 4286ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes if (insets.isConsumed()) { 4296ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes break; 4306ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 4316ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 4326ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 4336ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 434a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class LayoutParams extends LinearLayout.LayoutParams { 43550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 43650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** @hide */ 43750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @IntDef(flag=true, value={ 43850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_SCROLL, 43950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_EXIT_UNTIL_COLLAPSED, 44050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS, 44150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED 44250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes }) 44350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Retention(RetentionPolicy.SOURCE) 44450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public @interface ScrollFlags {} 44550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The view will be scroll in direct relation to scroll events. This flag needs to be 448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * set for any of the other flags to take effect. If any sibling views 449a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * before this one do not have this flag, then this value has no effect. 450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 451a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_SCROLL = 0x1; 452a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 453a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 454a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When exiting (scrolling off screen) the view will be scrolled until it is 455a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 'collapsed'. The collapsed height is defined by the view's minimum height. 456a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 457a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 458a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 459a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 460a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2; 461a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 462a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When entering (scrolling on screen) the view will scroll on any downwards 464a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll event, regardless of whether the scrolling view is also scrolling. This 465a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * is commonly referred to as the 'quick return' pattern. 466a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 467a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4; 468a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 469a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 470a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * An additional flag for 'enterAlways' which modifies the returning view to 471a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * only initially scroll back to it's collapsed height. Once the scrolling view has 472a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * reached the end of it's scroll range, the remainder of this view will be scrolled 473a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * into view. The collapsed height is defined by the view's minimum height. 474a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 475a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 476a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 477a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 478a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8; 479a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 480a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 481a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Internal flag which allows quick checking of 'quick return' 482a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 483a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS; 484a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 485a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int mScrollFlags = SCROLL_FLAG_SCROLL; 486a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Interpolator mScrollInterpolator; 487a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 488a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(Context c, AttributeSet attrs) { 489a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(c, attrs); 490a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams); 491a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0); 492a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) { 493a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int resId = a.getResourceId( 494a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0); 495a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator( 496a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes c, resId); 497a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 498a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 499a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 500a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 501a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height) { 502a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height); 503a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 504a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 505a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height, float weight) { 506a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height, weight); 507a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 508a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(ViewGroup.LayoutParams p) { 510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(p); 511a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 512a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 513a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(MarginLayoutParams source) { 514a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 515a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 516a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 517a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LinearLayout.LayoutParams source) { 518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 519a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 520a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 521a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LayoutParams source) { 522a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 523a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = source.mScrollFlags; 524a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = source.mScrollInterpolator; 525a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 526a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 527a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 528a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the scrolling flags. 529a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 530a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL}, 531a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS} 532a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * and {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED}. 533a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 534a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollFlags() 535a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 536149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags 537a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 53850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void setScrollFlags(@ScrollFlags int flags) { 539a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = flags; 540a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 541a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 542a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 543a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the scrolling flags. 544a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 545a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollFlags(int) 546a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 547149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags 548a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 54950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @ScrollFlags 550a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getScrollFlags() { 551a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollFlags; 552a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 553a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 554a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 555a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the interpolator to when scrolling the view associated with this 556a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. 557a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 558a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling. 559a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 560149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator 561a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollInterpolator() 562a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 563a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setScrollInterpolator(Interpolator interpolator) { 564a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = interpolator; 565a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 567a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the {@link Interpolator} being used for scrolling the view associated with this 569a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling. 570a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 571149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator 572a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollInterpolator(Interpolator) 573a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 574a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Interpolator getScrollInterpolator() { 575a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollInterpolator; 576a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 577a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 578a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 579a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 580a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested 581a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll handling with offsetting. 582a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 583a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class Behavior extends ViewOffsetBehavior<AppBarLayout> { 584e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private static final int INVALID_POSITION = -1; 585e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 586e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private int mOffsetDelta; 587a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 58850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private boolean mSkipNestedPreScroll; 589631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private Runnable mFlingRunnable; 590631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private ScrollerCompat mScroller; 591631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 592631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private ValueAnimatorCompat mAnimator; 59350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 594e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private int mOffsetToChildIndexOnLayout = INVALID_POSITION; 595e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private boolean mOffsetToChildIndexOnLayoutIsMinHeight; 596e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private float mOffsetToChildIndexOnLayoutPerc; 597e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior() {} 599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 600a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior(Context context, AttributeSet attrs) { 601a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 602a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 603a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 604a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 605a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 606a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View directTargetChild, View target, int nestedScrollAxes) { 607a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Return true if we're nested scrolling vertically and we have scrollable children 608631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 609a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes && child.hasScrollableChildren(); 610631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 611631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (started && mAnimator != null) { 612631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Cancel any offset animation 613631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.cancel(); 614631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 615631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 616631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return started; 617a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 618a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 619a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 620a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 621a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dx, int dy, int[] consumed) { 62250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy != 0 && !mSkipNestedPreScroll) { 62350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int min, max; 62450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy < 0) { 62550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling down 62650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getTotalScrollRange(); 62750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = min + child.getDownNestedPreScrollRange(); 62850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 62950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling up 63050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getUpNestedPreScrollRange(); 63150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = 0; 63250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 63350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes consumed[1] = scroll(coordinatorLayout, child, dy, min, max); 634a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 635a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 636a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 637a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 638a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 639a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dxConsumed, int dyConsumed, 640a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int dxUnconsumed, int dyUnconsumed) { 64150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dyUnconsumed < 0) { 64250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If the scrolling view is scrolling down but not consuming, it's probably be at 64350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // the top of it's content 64450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes scroll(coordinatorLayout, child, dyUnconsumed, 64550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes -child.getDownNestedScrollRange(), 0); 64650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Set the expanding flag so that onNestedPreScroll doesn't handle any events 64750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = true; 64850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 64950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // As we're no longer handling nested scrolls, reset the skip flag 65050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 651a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 65250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 653a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 65450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 65550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 65650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes View target) { 65750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Reset the skip flag 65850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 65950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 66050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 66150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 662631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public boolean onNestedFling(final CoordinatorLayout coordinatorLayout, 663631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final AppBarLayout child, View target, float velocityX, float velocityY, 664631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes boolean consumed) { 665631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (!consumed) { 666631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // It has been consumed so let's fling ourselves 667631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return fling(coordinatorLayout, child, -child.getTotalScrollRange(), 0, -velocityY); 668631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 669631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're scrolling up and the child also consumed the fling. We'll fake scroll 670631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // upto our 'collapsed' offset 671631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes int targetScroll; 672631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (velocityY < 0) { 673631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling down 674631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes targetScroll = -child.getTotalScrollRange() 675631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes + child.getDownNestedPreScrollRange(); 676631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 677631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (getTopBottomOffsetForScrollingSibling() > targetScroll) { 678631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're currently expanded more than the target scroll, we'll return false 679631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // now. This is so that we don't 'scroll' the wrong way. 680631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 681631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 682631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 683631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling up 684631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes targetScroll = -child.getUpNestedPreScrollRange(); 685631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 686631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (getTopBottomOffsetForScrollingSibling() < targetScroll) { 687631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're currently expanded less than the target scroll, we'll return 688631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // false now. This is so that we don't 'scroll' the wrong way. 689631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 690631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 691631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 692631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 693e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (getTopBottomOffsetForScrollingSibling() != targetScroll) { 694631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes animateOffsetTo(coordinatorLayout, child, targetScroll); 695631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return true; 696631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 697631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 698631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 699631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 700631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 701631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 702631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private void animateOffsetTo(final CoordinatorLayout coordinatorLayout, 703631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final AppBarLayout child, int offset) { 704631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mAnimator == null) { 705631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator = ViewUtils.createAnimator(); 706631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR); 707631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() { 708631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 709631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void onAnimationUpdate(ValueAnimatorCompat animator) { 710631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes setAppBarTopBottomOffset(coordinatorLayout, child, 711631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes animator.getAnimatedIntValue()); 712631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 713631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes }); 714631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 715631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.cancel(); 716631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 717631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 718631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setIntValues(getTopBottomOffsetForScrollingSibling(), offset); 719631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.start(); 720631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 721631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 722631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private boolean fling(CoordinatorLayout coordinatorLayout, AppBarLayout layout, int minOffset, 723631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes int maxOffset, float velocityY) { 724631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mFlingRunnable != null) { 725631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes layout.removeCallbacks(mFlingRunnable); 726631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 727631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 728631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mScroller == null) { 729631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mScroller = ScrollerCompat.create(layout.getContext()); 730631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 731631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 732631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mScroller.fling( 733e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 0, getTopBottomOffsetForScrollingSibling(), // curr 734631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 0, Math.round(velocityY), // velocity. 735631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 0, 0, // x 736631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes minOffset, maxOffset); // y 737631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 738631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mScroller.computeScrollOffset()) { 739631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mFlingRunnable = new FlingRunnable(coordinatorLayout, layout); 740631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.postOnAnimation(layout, mFlingRunnable); 741631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return true; 742631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 743631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mFlingRunnable = null; 744631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 745631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 746631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 747631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 748631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private class FlingRunnable implements Runnable { 749631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private final CoordinatorLayout mParent; 750631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private final AppBarLayout mLayout; 751631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 752631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes FlingRunnable(CoordinatorLayout parent, AppBarLayout layout) { 753631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mParent = parent; 754631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mLayout = layout; 755631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 756631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 757631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 758631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void run() { 759631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mLayout != null && mScroller != null && mScroller.computeScrollOffset()) { 760631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes setAppBarTopBottomOffset(mParent, mLayout, mScroller.getCurrY()); 761631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 762631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Post ourselves so that we run on the next animation 763631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.postOnAnimation(mLayout, this); 764631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 765631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 766631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 767631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 768631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 769e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout appBarLayout, 77050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int layoutDirection) { 771e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes boolean handled = super.onLayoutChild(parent, appBarLayout, layoutDirection); 772e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 773e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (mOffsetToChildIndexOnLayout >= 0) { 774e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes View child = appBarLayout.getChildAt(mOffsetToChildIndexOnLayout); 775e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes int offset = -child.getBottom(); 776e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (mOffsetToChildIndexOnLayoutIsMinHeight) { 777e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes offset += ViewCompat.getMinimumHeight(child); 778e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } else { 779e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc); 780e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 781e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes setTopAndBottomOffset(offset); 782e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = INVALID_POSITION; 783e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 78450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 78550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Make sure we update the elevation 786e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dispatchOffsetUpdates(appBarLayout); 78750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 78850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return handled; 789a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 790a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 791a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout, 79250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int dy, int minOffset, int maxOffset) { 793a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, 794e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes getTopBottomOffsetForScrollingSibling() - dy, minOffset, maxOffset); 795a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 796a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 797631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout, 798631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes AppBarLayout appBarLayout, int newOffset) { 799631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, newOffset, 800631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes Integer.MIN_VALUE, Integer.MAX_VALUE); 801631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 802631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 803631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout, 80450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) { 805e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int curOffset = getTopBottomOffsetForScrollingSibling(); 806a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int consumed = 0; 807a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 808631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) { 809631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we have some scrolling range, and we're currently within the min and max 810631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // offsets, calculate a new offset 81150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset); 812a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 813a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (curOffset != newOffset) { 814e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int interpolatedOffset = appBarLayout.hasChildWithInterpolator() 815e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ? interpolateOffset(appBarLayout, newOffset) 816e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes : newOffset; 817e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 818e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset); 819631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 820a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update how much dy we have consumed 821a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes consumed = curOffset - newOffset; 822a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update the stored sibling offset 823e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetDelta = newOffset - interpolatedOffset; 824a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 825a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) { 826a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the offset hasn't changed and we're using an interpolated scroll 827a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // then we need to keep any dependent views updated. CoL will do this for 828a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us when we move, but we need to do it manually when we don't (as an 829a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // interpolated scroll may finish early). 830a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes coordinatorLayout.dispatchDependentViewsChanged(appBarLayout); 831a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 832a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 833631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Dispatch the updates to any listeners 834631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes dispatchOffsetUpdates(appBarLayout); 835a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 836a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 837a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 838a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return consumed; 839a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 840a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 841631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private void dispatchOffsetUpdates(AppBarLayout layout) { 842631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final List<WeakReference<OnOffsetChangedListener>> listeners = layout.mListeners; 84350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 844631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Iterate backwards through the list so that most recently added listeners 845631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // get the first chance to decide 846631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes for (int i = 0, z = listeners.size(); i < z; i++) { 847631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final WeakReference<OnOffsetChangedListener> ref = listeners.get(i); 848631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final OnOffsetChangedListener listener = ref != null ? ref.get() : null; 84950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 850631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (listener != null) { 851631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes listener.onOffsetChanged(layout, getTopAndBottomOffset()); 852a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 853a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 854a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 855a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 856a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int interpolateOffset(AppBarLayout layout, final int offset) { 857a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int absOffset = Math.abs(offset); 858a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 859a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 860a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = layout.getChildAt(i); 861a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 86250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 863a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 86450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (absOffset >= child.getTop() && absOffset <= child.getBottom()) { 865a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 86650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int childScrollableHeight = 0; 86750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int flags = childLp.getScrollFlags(); 86850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 86950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're set to scroll so add the child's height 87050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight += child.getHeight(); 87150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 87250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 87350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight -= ViewCompat.getMinimumHeight(child); 87450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 87550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 87650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 87750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childScrollableHeight > 0) { 87850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int offsetForView = absOffset - child.getTop(); 87950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int interpolatedDiff = Math.round(childScrollableHeight * 88050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes interpolator.getInterpolation( 88150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes offsetForView / (float) childScrollableHeight)); 88250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 88350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return Integer.signum(offset) * (child.getTop() + interpolatedDiff); 88450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 885a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 88650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 88750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If we get to here then the view on the offset isn't suitable for interpolated 88850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // scrolling. So break out of the loop 88950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes break; 890a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 891a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 892a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 893a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return offset; 894a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 895a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 896a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getTopBottomOffsetForScrollingSibling() { 897e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return getTopAndBottomOffset() + mOffsetDelta; 898e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 899e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 900e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 901e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout) { 902e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final Parcelable superState = super.onSaveInstanceState(parent, appBarLayout); 903e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int offset = getTopAndBottomOffset(); 904e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 905e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes // Try and find the first visible child... 906e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes for (int i = 0, count = appBarLayout.getChildCount(); i < count; i++) { 907e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes View child = appBarLayout.getChildAt(i); 908e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int visBottom = child.getBottom() + offset; 909e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 910e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (child.getTop() + offset <= 0 && visBottom >= 0) { 911e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final SavedState ss = new SavedState(superState); 912e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibleChildIndex = i; 913e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibileChildAtMinimumHeight = 914e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes visBottom == ViewCompat.getMinimumHeight(child); 915e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibileChildPercentageShown = visBottom / (float) child.getHeight(); 916e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return ss; 917e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 918e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 919e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 920e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes // Else we'll just return the super state 921e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return superState; 922e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 923e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 924e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 925e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout, 926e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes Parcelable state) { 927e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (state instanceof SavedState) { 928e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final SavedState ss = (SavedState) state; 929e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.onRestoreInstanceState(parent, appBarLayout, ss.getSuperState()); 930e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = ss.firstVisibleChildIndex; 931e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayoutPerc = ss.firstVisibileChildPercentageShown; 932e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayoutIsMinHeight = ss.firstVisibileChildAtMinimumHeight; 933e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } else { 934e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.onRestoreInstanceState(parent, appBarLayout, state); 935e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = INVALID_POSITION; 936e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 937e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 938e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 939e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes protected static class SavedState extends View.BaseSavedState { 940e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes int firstVisibleChildIndex; 941e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes float firstVisibileChildPercentageShown; 942e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes boolean firstVisibileChildAtMinimumHeight; 943e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 944e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState(Parcel source) { 945e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super(source); 946e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibleChildIndex = source.readInt(); 947e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibileChildPercentageShown = source.readFloat(); 948e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibileChildAtMinimumHeight = source.readByte() != 0; 949e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 950e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 951e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState(Parcelable superState) { 952e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super(superState); 953e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 954e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 955e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 956e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public void writeToParcel(Parcel dest, int flags) { 957e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.writeToParcel(dest, flags); 958e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeInt(firstVisibleChildIndex); 959e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeFloat(firstVisibileChildPercentageShown); 960e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeByte((byte) (firstVisibileChildAtMinimumHeight ? 1 : 0)); 961e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 962e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 963e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public static final Parcelable.Creator<SavedState> CREATOR = 964e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes new Parcelable.Creator<SavedState>() { 965e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 966e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState createFromParcel(Parcel source) { 967e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return new SavedState(source); 968e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 969e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 970e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 971e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState[] newArray(int size) { 972e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return new SavedState[size]; 973e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 974e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes }; 975a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 976a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 977a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 978a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 979a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Behavior which should be used by {@link View}s which can scroll vertically and support 980a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * nested scrolling to automatically scroll any {@link AppBarLayout} siblings. 981a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 982a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class ScrollingViewBehavior extends ViewOffsetBehavior<View> { 983a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mOverlayTop; 984a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 985a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior() {} 986a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 987a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior(Context context, AttributeSet attrs) { 988a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 989a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 990a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, 991a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params); 992a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = a.getDimensionPixelSize( 993a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0); 994a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 995a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 996a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 997a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 998a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { 999a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We depend on any AppBarLayouts 1000a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return dependency instanceof AppBarLayout; 1001a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1002a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1003a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1004a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onMeasureChild(CoordinatorLayout parent, View child, 1005a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentWidthMeasureSpec, int widthUsed, 1006a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentHeightMeasureSpec, int heightUsed) { 1007a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (child.getLayoutParams().height == LayoutParams.MATCH_PARENT) { 1008a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the child's height is set to match_parent then it with it's maximum visible 1009a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // visible height 1010a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1011a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final List<View> dependencies = parent.getDependencies(child); 1012a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dependencies.isEmpty()) { 1013a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we don't have any dependencies, return false 1014a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 1015a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1016a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1017a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout appBar = findFirstAppBarLayout(dependencies); 10186ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes if (appBar != null && ViewCompat.isLaidOut(appBar)) { 10196ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes if (ViewCompat.getFitsSystemWindows(appBar)) { 10206ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // If the AppBarLayout is fitting system windows then we need to also, 10216ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // otherwise we'll get CoL's compatible layout functionality 10226ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes ViewCompat.setFitsSystemWindows(child, true); 1023a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1024a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1025a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollRange = appBar.getTotalScrollRange(); 10266ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes final int height = parent.getHeight() 1027a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes - appBar.getMeasuredHeight() + scrollRange; 1028a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 1029a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes MeasureSpec.AT_MOST); 1030a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1031a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Now measure the scrolling child with the correct height 1032a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes parent.onMeasureChild(child, parentWidthMeasureSpec, 1033a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes widthUsed, heightMeasureSpec, heightUsed); 1034a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1035a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return true; 1036a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1037a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1038a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 1039a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1040a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1041a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1042a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onDependentViewChanged(CoordinatorLayout parent, View child, 1043a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View dependency) { 1044a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final CoordinatorLayout.Behavior behavior = 1045a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); 1046a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (behavior instanceof Behavior) { 1047a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Offset the child so that it is below the app-bar (with any overlap) 1048a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1049a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int appBarOffset = ((Behavior) behavior) 1050a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes .getTopBottomOffsetForScrollingSibling(); 1051a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int expandedMax = dependency.getHeight() - mOverlayTop; 1052a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int collapsedMin = parent.getHeight() - child.getHeight(); 1053a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1054a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mOverlayTop != 0 && dependency instanceof AppBarLayout) { 1055a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we have an overlap top, and the dependency is an AppBarLayout, we control 1056a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the offset ourselves based on the appbar's scroll progress. This is so that 1057a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the scroll happens sequentially rather than linearly 1058a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange(); 1059a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin, 1060a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Math.abs(appBarOffset) / (float) scrollRange)); 1061a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 1062a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setTopAndBottomOffset(MathUtils.constrain( 1063a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes dependency.getHeight() - mOverlayTop + appBarOffset, 1064a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes collapsedMin, expandedMax)); 1065a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1066a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1067a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 1068a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1069a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1070a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 1071a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the distance that this view should overlap any {@link AppBarLayout}. 1072a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 1073a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param overlayTop the distance in px 1074a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 1075a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOverlayTop(int overlayTop) { 1076a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = overlayTop; 1077a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1078a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1079a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 1080a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the distance that this view should overlap any {@link AppBarLayout}. 1081a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 1082a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getOverlayTop() { 1083a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mOverlayTop; 1084a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1085a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1086a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static AppBarLayout findFirstAppBarLayout(List<View> views) { 1087a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = views.size(); i < z; i++) { 1088a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View view = views.get(i); 1089a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (view instanceof AppBarLayout) { 1090a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return (AppBarLayout) view; 1091a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1092a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1093a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return null; 1094a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1095a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1096a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes} 1097