AppBarLayout.java revision ee01b9bca75c41beddce986596a91ecec12eca5e
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 198e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 208e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.design.widget.ViewUtils.objectEquals; 218e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas 228f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viveretteimport android.annotation.TargetApi; 23a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.Context; 24a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.res.TypedArray; 2500a00a7d3ba8279294f63994473afc32e05dcf10Chris Banesimport android.graphics.Rect; 261a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banesimport android.os.Build; 27e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcel; 28e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcelable; 2950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport android.support.annotation.IntDef; 3072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.annotation.NonNull; 3172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.annotation.Nullable; 328f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viveretteimport android.support.annotation.RequiresApi; 33c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo; 3413633353237d6506f4c436b459bc8be8c3d7ed68Chris Banesimport android.support.annotation.VisibleForTesting; 35a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.design.R; 3662ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banesimport android.support.v4.os.ParcelableCompat; 3762ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banesimport android.support.v4.os.ParcelableCompatCreatorCallbacks; 3805f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banesimport android.support.v4.view.AbsSavedState; 39a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat; 406ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banesimport android.support.v4.view.WindowInsetsCompat; 41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet; 42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View; 43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup; 44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator; 45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout; 46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 4750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.Retention; 4850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.RetentionPolicy; 49d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banesimport java.lang.ref.WeakReference; 50631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.ArrayList; 51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List; 52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/** 54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of 559fb154338a62edc2c57dc036895199d6f1769400Chris Banes * material designs app bar concept, namely scrolling gestures. 56a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 57a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through 58a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute: 59a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}. 60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}. 63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will 64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work. 65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 669fb154338a62edc2c57dc036895199d6f1769400Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to know when to scroll. 679fb154338a62edc2c57dc036895199d6f1769400Chris Banes * The binding is done through the {@link ScrollingViewBehavior} behavior class, meaning that you 689fb154338a62edc2c57dc036895199d6f1769400Chris Banes * should set your scrolling view's behavior to be an instance of {@link ScrollingViewBehavior}. 699fb154338a62edc2c57dc036895199d6f1769400Chris Banes * A string resource containing the full class name is available. 70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre> 72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.CoordinatorLayout 73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:android="http://schemas.android.com/apk/res/android" 74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:app="http://schemas.android.com/apk/res-auto" 75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent"> 77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v4.widget.NestedScrollView 79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent" 81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_behavior="@string/appbar_scrolling_view_behavior"> 82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <!-- Your scrolling content --> 84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.v4.widget.NestedScrollView> 86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.AppBarLayout 88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="wrap_content" 89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent"> 90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v7.widget.Toolbar 92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.TabLayout 96a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 97a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 99a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.AppBarLayout> 100a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.CoordinatorLayout> 102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre> 103a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar"> 105a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a> 106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class) 108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout { 109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 110657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas static final int PENDING_ACTION_NONE = 0x0; 111657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas static final int PENDING_ACTION_EXPANDED = 0x1; 112657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas static final int PENDING_ACTION_COLLAPSED = 0x2; 113657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4; 1142bab57ed97f284a8152d180cca796739afa3a71dChris Banes static final int PENDING_ACTION_FORCE = 0x8; 1159995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 117631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical 118631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * offset changes. 119a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 120631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public interface OnOffsetChangedListener { 121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 122a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows 123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * child views to implement custom behavior based on the offset (for instance pinning a 124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view at a certain y value). 125a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 126631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param appBarLayout the {@link AppBarLayout} which offset has changed 12750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px 128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 129631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset); 130a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 131a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 132a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static final int INVALID_SCROLL_RANGE = -1; 133a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 134a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mTotalScrollRange = INVALID_SCROLL_RANGE; 135a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownPreScrollRange = INVALID_SCROLL_RANGE; 136a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownScrollRange = INVALID_SCROLL_RANGE; 137a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1381a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private boolean mHaveChildWithInterpolator; 13950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 1409995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private int mPendingAction = PENDING_ACTION_NONE; 1419995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 1426ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes private WindowInsetsCompat mLastInsets; 1436ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 1441a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private List<OnOffsetChangedListener> mListeners; 1451a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 1461a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private boolean mCollapsible; 1471a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private boolean mCollapsed; 1481a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 149ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov private int[] mTmpStatesArray; 150631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 151a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context) { 152a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes this(context, null); 153a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 154a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 155a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context, AttributeSet attrs) { 156a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 157a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setOrientation(VERTICAL); 15850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 159809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes ThemeUtils.checkAppCompatTheme(context); 160809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes 1611a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (Build.VERSION.SDK_INT >= 21) { 1621a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // Use the bounds view outline provider so that we cast a shadow, even without a 1631a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // background 1641a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes ViewUtilsLollipop.setBoundsViewOutlineProvider(this); 1651a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 1661a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // If we're running on API 21+, we should reset any state list animator from our 1671a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // default style 1681a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes ViewUtilsLollipop.setStateListAnimatorFromAttrs(this, attrs, 0, 1691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes R.style.Widget_Design_AppBarLayout); 1701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 1711a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 1721a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout, 17350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 0, R.style.Widget_Design_AppBarLayout); 17447082c30c630c34829439a9eecd1cf7e8d255a86Aurimas Liutikas ViewCompat.setBackground(this, a.getDrawable(R.styleable.AppBarLayout_android_background)); 1759995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (a.hasValue(R.styleable.AppBarLayout_expanded)) { 1762bab57ed97f284a8152d180cca796739afa3a71dChris Banes setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false), false, false); 1779995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 1781a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (Build.VERSION.SDK_INT >= 21 && a.hasValue(R.styleable.AppBarLayout_elevation)) { 1791a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator( 1801a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes this, a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0)); 1811a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 18250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes a.recycle(); 18350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 1846ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes ViewCompat.setOnApplyWindowInsetsListener(this, 1856ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes new android.support.v4.view.OnApplyWindowInsetsListener() { 1866ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes @Override 1876ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes public WindowInsetsCompat onApplyWindowInsets(View v, 1886ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes WindowInsetsCompat insets) { 1898818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes return onWindowInsetChanged(insets); 1906ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 1916ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes }); 192631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 193631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 194631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 195631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Add a listener that will be called when the offset of this {@link AppBarLayout} changes. 196631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 197631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener The listener that will be called when the offset changes.] 198631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 199631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @see #removeOnOffsetChangedListener(OnOffsetChangedListener) 200631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 201631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void addOnOffsetChangedListener(OnOffsetChangedListener listener) { 2021a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (mListeners == null) { 2031a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes mListeners = new ArrayList<>(); 2041a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 2056f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes if (listener != null && !mListeners.contains(listener)) { 2066f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes mListeners.add(listener); 207631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 208631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 209631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 210631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 211631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Remove the previously added {@link OnOffsetChangedListener}. 212631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 213631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener the listener to remove. 214631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 215631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) { 2161a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (mListeners != null && listener != null) { 2176f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes mListeners.remove(listener); 218631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 222ea004a01f44aaf36212120c936952c1742d03d30Chris Banes protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 223ea004a01f44aaf36212120c936952c1742d03d30Chris Banes super.onMeasure(widthMeasureSpec, heightMeasureSpec); 224ea004a01f44aaf36212120c936952c1742d03d30Chris Banes invalidateScrollRanges(); 225ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 226ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 227ea004a01f44aaf36212120c936952c1742d03d30Chris Banes @Override 228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected void onLayout(boolean changed, int l, int t, int r, int b) { 229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.onLayout(changed, l, t, r, b); 23079aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes invalidateScrollRanges(); 231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = false; 233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 234a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 235a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 237a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 238a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 239a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = true; 240a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 241a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 242a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 2431a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 2441a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes updateCollapsible(); 2451a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 2461a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 2471a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private void updateCollapsible() { 2481a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes boolean haveCollapsibleChild = false; 2491a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 2501a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (((LayoutParams) getChildAt(i).getLayoutParams()).isCollapsible()) { 2511a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes haveCollapsibleChild = true; 2521a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes break; 2531a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 2541a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 25557aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes setCollapsibleState(haveCollapsibleChild); 256a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 258ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private void invalidateScrollRanges() { 259ea004a01f44aaf36212120c936952c1742d03d30Chris Banes // Invalidate the scroll ranges 260ea004a01f44aaf36212120c936952c1742d03d30Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 261ea004a01f44aaf36212120c936952c1742d03d30Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 262ea004a01f44aaf36212120c936952c1742d03d30Chris Banes mDownScrollRange = INVALID_SCROLL_RANGE; 263ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 264ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 265a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 266a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOrientation(int orientation) { 267a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (orientation != VERTICAL) { 268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes throw new IllegalArgumentException("AppBarLayout is always vertical and does" 269a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes + " not support horizontal orientation"); 270a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.setOrientation(orientation); 272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 2749995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes /** 2759995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * Sets whether this {@link AppBarLayout} is expanded or not, animating if it has already 2769995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * been laid out. 2779995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2789995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a 2799995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * direct child of a {@link CoordinatorLayout}.</p> 2809995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2819995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param expanded true if the layout should be fully expanded, false if it should 2829995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * be fully collapsed 2839995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2849995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_expanded 2859995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes */ 2869995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public void setExpanded(boolean expanded) { 2879995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes setExpanded(expanded, ViewCompat.isLaidOut(this)); 2889995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 2899995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 2909995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes /** 2919995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * Sets whether this {@link AppBarLayout} is expanded or not. 2929995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2939995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a 2949995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * direct child of a {@link CoordinatorLayout}.</p> 2959995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2969995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param expanded true if the layout should be fully expanded, false if it should 2979995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * be fully collapsed 2989995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param animate Whether to animate to the new state 2999995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 3009995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_expanded 3019995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes */ 3029995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public void setExpanded(boolean expanded, boolean animate) { 3032bab57ed97f284a8152d180cca796739afa3a71dChris Banes setExpanded(expanded, animate, true); 3042bab57ed97f284a8152d180cca796739afa3a71dChris Banes } 3052bab57ed97f284a8152d180cca796739afa3a71dChris Banes 3062bab57ed97f284a8152d180cca796739afa3a71dChris Banes private void setExpanded(boolean expanded, boolean animate, boolean force) { 3079995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mPendingAction = (expanded ? PENDING_ACTION_EXPANDED : PENDING_ACTION_COLLAPSED) 3082bab57ed97f284a8152d180cca796739afa3a71dChris Banes | (animate ? PENDING_ACTION_ANIMATE_ENABLED : 0) 3092bab57ed97f284a8152d180cca796739afa3a71dChris Banes | (force ? PENDING_ACTION_FORCE : 0); 3109995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes requestLayout(); 3119995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 3129995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return p instanceof LayoutParams; 316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateDefaultLayoutParams() { 320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 323a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams generateLayoutParams(AttributeSet attrs) { 325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(getContext(), attrs); 326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 3308f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette if (Build.VERSION.SDK_INT >= 19 && p instanceof LinearLayout.LayoutParams) { 331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((LinearLayout.LayoutParams) p); 332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (p instanceof MarginLayoutParams) { 333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((MarginLayoutParams) p); 334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(p); 336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 338657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas boolean hasChildWithInterpolator() { 339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mHaveChildWithInterpolator; 340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 3436ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes * Returns the scroll range of all children. 344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @return the scroll range in px 346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 3476ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes public final int getTotalScrollRange() { 348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mTotalScrollRange != INVALID_SCROLL_RANGE) { 349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange; 350a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 351a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 352a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 356ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int childHeight = child.getMeasuredHeight(); 357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 361b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes range += childHeight + lp.topMargin + lp.bottomMargin; 362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 3656ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // We also break straight away since later views can't scroll beneath 366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us 3676ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes range -= ViewCompat.getMinimumHeight(child); 3686ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes break; 369a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 370a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 371a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 372a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 373a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 3764832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes return mTotalScrollRange = Math.max(0, range - getTopInset()); 377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 378a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 379657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas boolean hasScrollableChildren() { 380a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return getTotalScrollRange() != 0; 381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 384a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling up from a nested pre-scroll. 385a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 386657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas int getUpNestedPreScrollRange() { 38750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return getTotalScrollRange(); 388a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 389a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 390a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 391a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested pre-scroll. 392a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 393657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas int getDownNestedPreScrollRange() { 394a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownPreScrollRange != INVALID_SCROLL_RANGE) { 395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange; 397a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 398a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 399a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 400a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 403ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int childHeight = child.getMeasuredHeight(); 404a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 405a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 406a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) { 407b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // First take the margin into account 408b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes range += lp.topMargin + lp.bottomMargin; 409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // The view has the quick return flag combination... 410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) { 411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If they're set to enter collapsed, use the minimum height 412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += ViewCompat.getMinimumHeight(child); 41347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes } else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 41447543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes // Only enter by the amount of the collapsed height 41547543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes range += childHeight - ViewCompat.getMinimumHeight(child); 416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 417116332176fa904af2f701011de8d4f2524c10a8eChris Banes // Else use the full height (minus the top inset) 418116332176fa904af2f701011de8d4f2524c10a8eChris Banes range += childHeight - getTopInset(); 419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 420a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (range > 0) { 421a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we've hit an non-quick return scrollable view, and we've already hit a 422a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // quick return view, return now 423a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 424a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 425a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 42613633353237d6506f4c436b459bc8be8c3d7ed68Chris Banes return mDownPreScrollRange = Math.max(0, range); 427a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 428a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 429a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 430a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested scroll. 431a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 432657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas int getDownNestedScrollRange() { 433a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownScrollRange != INVALID_SCROLL_RANGE) { 434a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 435a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange; 436a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 437a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 438a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 439c1ce4f6cf4d99d6856c4259bf1bb1cb56f604737Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 440a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 441a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 442ea004a01f44aaf36212120c936952c1742d03d30Chris Banes int childHeight = child.getMeasuredHeight(); 443b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes childHeight += lp.topMargin + lp.bottomMargin; 444a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 449a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 451a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 452a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing exit scroll, we to take the collapsed height into account. 4534832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes // We also break the range straight away since later views can't scroll 454a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // beneath us 4554832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes range -= ViewCompat.getMinimumHeight(child) + getTopInset(); 4564832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes break; 457a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 458a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 459a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 460a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 461a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 462a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 4644c33be829067714342b629d29329206bc2116afeChris Banes return mDownScrollRange = Math.max(0, range); 465a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 466a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 467657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas void dispatchOffsetUpdates(int offset) { 4681a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // Iterate backwards through the list so that most recently added listeners 4691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // get the first chance to decide 4701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (mListeners != null) { 4711a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes for (int i = 0, z = mListeners.size(); i < z; i++) { 4721a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final OnOffsetChangedListener listener = mListeners.get(i); 4731a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (listener != null) { 4741a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes listener.onOffsetChanged(this, offset); 4751a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 4761a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 4771a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 4781a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 4791a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 48050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int getMinimumHeightForVisibleOverlappingContent() { 48117ed3263761329f6aa6796941358c41001fff325Chris Banes final int topInset = getTopInset(); 48250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int minHeight = ViewCompat.getMinimumHeight(this); 48350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (minHeight != 0) { 48450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If this layout has a min height, use it (doubled) 4856ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes return (minHeight * 2) + topInset; 48650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 48750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 48850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Otherwise, we'll use twice the min height of our last child 48950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childCount = getChildCount(); 490b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes final int lastChildMinHeight = childCount >= 1 491b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) : 0; 492b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes if (lastChildMinHeight != 0) { 493b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes return (lastChildMinHeight * 2) + topInset; 494b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes } 495b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes 496b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes // If we reach here then we don't have a min height explicitly set. Instead we'll take a 497b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes // guess at 1/3 of our height being visible 498b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes return getHeight() / 3; 49950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 50050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 5011a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes @Override 5021a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes protected int[] onCreateDrawableState(int extraSpace) { 503ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov if (mTmpStatesArray == null) { 504ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov // Note that we can't allocate this at the class level (in declaration) since 505ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov // some paths in super View constructor are going to call this method before 506ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov // that 507ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov mTmpStatesArray = new int[2]; 508ee01b9bca75c41beddce986596a91ecec12eca5eKirill Grouchnikov } 5091a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final int[] extraStates = mTmpStatesArray; 5101a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final int[] states = super.onCreateDrawableState(extraSpace + extraStates.length); 5111a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 5121a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes extraStates[0] = mCollapsible ? R.attr.state_collapsible : -R.attr.state_collapsible; 5131a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes extraStates[1] = mCollapsible && mCollapsed 5141a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes ? R.attr.state_collapsed : -R.attr.state_collapsed; 5151a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 5161a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes return mergeDrawableStates(states, extraStates); 5171a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 5181a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 519c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes /** 520c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes * Sets whether the AppBarLayout has collapsible children or not. 521c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes * 522c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes * @return true if the collapsible state changed 523c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes */ 524c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes private boolean setCollapsibleState(boolean collapsible) { 5251a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (mCollapsible != collapsible) { 5261a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes mCollapsible = collapsible; 5271a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes refreshDrawableState(); 528c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes return true; 5291a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 530c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes return false; 5311a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 5321a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 533c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes /** 534c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes * Sets whether the AppBarLayout is in a collapsed state or not. 535c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes * 536c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes * @return true if the collapsed state changed 537c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes */ 538657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas boolean setCollapsedState(boolean collapsed) { 5391a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (mCollapsed != collapsed) { 5401a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes mCollapsed = collapsed; 5411a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes refreshDrawableState(); 542c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes return true; 5431a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 544c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes return false; 5451a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 5461a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 54750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** 5481a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes * @deprecated target elevation is now deprecated. AppBarLayout's elevation is now 549926cd28257536b1206934d69585a2dab100147d5Chris Banes * controlled via a {@link android.animation.StateListAnimator}. If a target 550c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas * elevation is set, either by this method or the {@code app:elevation} attribute, 551926cd28257536b1206934d69585a2dab100147d5Chris Banes * a new state list animator is created which uses the given {@code elevation} value. 552631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 5531a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_elevation 55450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes */ 5551a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes @Deprecated 556631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void setTargetElevation(float elevation) { 5571a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (Build.VERSION.SDK_INT >= 21) { 5581a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(this, elevation); 5591a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 560631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 561631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 562631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 5631a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes * @deprecated target elevation is now deprecated. AppBarLayout's elevation is now 564926cd28257536b1206934d69585a2dab100147d5Chris Banes * controlled via a {@link android.animation.StateListAnimator}. This method now 565926cd28257536b1206934d69585a2dab100147d5Chris Banes * always returns 0. 566631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 5671a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes @Deprecated 568631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public float getTargetElevation() { 5691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes return 0; 57050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 57150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 572657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas int getPendingAction() { 5739995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes return mPendingAction; 5749995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 5759995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 576657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas void resetPendingAction() { 5779995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mPendingAction = PENDING_ACTION_NONE; 5789995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 5799995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 58013633353237d6506f4c436b459bc8be8c3d7ed68Chris Banes @VisibleForTesting 58113633353237d6506f4c436b459bc8be8c3d7ed68Chris Banes final int getTopInset() { 582ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; 583ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 584ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 585657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas WindowInsetsCompat onWindowInsetChanged(final WindowInsetsCompat insets) { 5868818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes WindowInsetsCompat newInsets = null; 5878818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes 5888818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes if (ViewCompat.getFitsSystemWindows(this)) { 5898818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes // If we're set to fit system windows, keep the insets 5908818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes newInsets = insets; 5918818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes } 5928818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes 5938818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes // If our insets have changed, keep them and invalidate the scroll ranges... 59419a80415a88ecfae6bbc645cd14b653ed4337648Chris Banes if (!objectEquals(mLastInsets, newInsets)) { 5958818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes mLastInsets = newInsets; 5968818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes invalidateScrollRanges(); 5978818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes } 5988818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes 59917ed3263761329f6aa6796941358c41001fff325Chris Banes return insets; 6006ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 6016ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 602a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class LayoutParams extends LinearLayout.LayoutParams { 60350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 60450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** @hide */ 6058e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 60650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @IntDef(flag=true, value={ 60750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_SCROLL, 60850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_EXIT_UNTIL_COLLAPSED, 60950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS, 610bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED, 611bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes SCROLL_FLAG_SNAP 61250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes }) 61350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Retention(RetentionPolicy.SOURCE) 61450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public @interface ScrollFlags {} 61550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 616a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 617a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The view will be scroll in direct relation to scroll events. This flag needs to be 618a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * set for any of the other flags to take effect. If any sibling views 619a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * before this one do not have this flag, then this value has no effect. 620a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 621a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_SCROLL = 0x1; 622a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 623a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 624a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When exiting (scrolling off screen) the view will be scrolled until it is 625a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 'collapsed'. The collapsed height is defined by the view's minimum height. 626a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 627a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 628a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 629a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 630a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2; 631a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 632a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 633a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When entering (scrolling on screen) the view will scroll on any downwards 634a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll event, regardless of whether the scrolling view is also scrolling. This 635a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * is commonly referred to as the 'quick return' pattern. 636a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 637a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4; 638a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 639a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 640a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * An additional flag for 'enterAlways' which modifies the returning view to 641a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * only initially scroll back to it's collapsed height. Once the scrolling view has 642a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * reached the end of it's scroll range, the remainder of this view will be scrolled 643a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * into view. The collapsed height is defined by the view's minimum height. 644a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 645a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 646a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 647a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 648a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8; 649a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 650a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 651bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * Upon a scroll ending, if the view is only partially visible then it will be snapped 652bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * and scrolled to it's closest edge. For example, if the view only has it's bottom 25% 653bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * displayed, it will be scrolled off screen completely. Conversely, if it's bottom 75% 654bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * is visible then it will be scrolled fully into view. 655bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes */ 656bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes public static final int SCROLL_FLAG_SNAP = 0x10; 657bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 658bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes /** 659bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * Internal flags which allows quick checking features 660a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 661a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS; 662bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes static final int FLAG_SNAP = SCROLL_FLAG_SCROLL | SCROLL_FLAG_SNAP; 6631a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes static final int COLLAPSIBLE_FLAGS = SCROLL_FLAG_EXIT_UNTIL_COLLAPSED 6641a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes | SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED; 665a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 666a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int mScrollFlags = SCROLL_FLAG_SCROLL; 667a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Interpolator mScrollInterpolator; 668a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 669a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(Context c, AttributeSet attrs) { 670a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(c, attrs); 671c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_Layout); 672c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes mScrollFlags = a.getInt(R.styleable.AppBarLayout_Layout_layout_scrollFlags, 0); 673c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes if (a.hasValue(R.styleable.AppBarLayout_Layout_layout_scrollInterpolator)) { 674a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int resId = a.getResourceId( 675c6cbbb50a06ec08e6888bd1c3dfd7fdf588d9db5Chris Banes R.styleable.AppBarLayout_Layout_layout_scrollInterpolator, 0); 676a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator( 677a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes c, resId); 678a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 679a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 680a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 681a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 682a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height) { 683a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height); 684a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 685a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 686a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height, float weight) { 687a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height, weight); 688a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 689a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 690a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(ViewGroup.LayoutParams p) { 691a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(p); 692a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 693a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 694a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(MarginLayoutParams source) { 695a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 696a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 697a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 6988f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette @RequiresApi(19) 6998f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette @TargetApi(19) 700a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LinearLayout.LayoutParams source) { 7018f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette // The copy constructor called here only exists on API 19+. 702a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 703a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 704a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 7058f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette @RequiresApi(19) 7068f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette @TargetApi(19) 707a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LayoutParams source) { 7088f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette // The copy constructor called here only exists on API 19+. 709a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 710a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = source.mScrollFlags; 711a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = source.mScrollInterpolator; 712a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 713a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 714a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 715a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the scrolling flags. 716a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 717a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL}, 718bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS}, 719bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED} and {@link #SCROLL_FLAG_SNAP }. 720a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 721a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollFlags() 722a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 72373e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollFlags 724a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 72550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void setScrollFlags(@ScrollFlags int flags) { 726a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = flags; 727a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 728a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 729a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 730a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the scrolling flags. 731a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 732a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollFlags(int) 733a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 73473e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollFlags 735a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 73650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @ScrollFlags 737a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getScrollFlags() { 738a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollFlags; 739a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 740a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 741a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 742a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the interpolator to when scrolling the view associated with this 743a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. 744a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 745a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling. 746a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 74773e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollInterpolator 748a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollInterpolator() 749a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 750a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setScrollInterpolator(Interpolator interpolator) { 751a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = interpolator; 752a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 753a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 754a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 755a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the {@link Interpolator} being used for scrolling the view associated with this 756a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling. 757a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 75873e7a06fb66110495fa4e98a91831a47feab2526Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollInterpolator 759a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollInterpolator(Interpolator) 760a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 761a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Interpolator getScrollInterpolator() { 762a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollInterpolator; 763a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 7641a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 7651a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes /** 7661a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes * Returns true if the scroll flags are compatible for 'collapsing' 7671a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes */ 768657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas boolean isCollapsible() { 7691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes return (mScrollFlags & SCROLL_FLAG_SCROLL) == SCROLL_FLAG_SCROLL 7701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes && (mScrollFlags & COLLAPSIBLE_FLAGS) != 0; 7711a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 772a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 773a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 774a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 775a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested 776a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll handling with offsetting. 777a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 778ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor public static class Behavior extends HeaderBehavior<AppBarLayout> { 77957aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes private static final int MAX_OFFSET_ANIMATION_DURATION = 600; // ms 780e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private static final int INVALID_POSITION = -1; 781e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 78272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes /** 78372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * Callback to allow control over any {@link AppBarLayout} dragging. 78472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes */ 78572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes public static abstract class DragCallback { 78672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes /** 78772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * Allows control over whether the given {@link AppBarLayout} can be dragged or not. 78872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * 78972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * <p>Dragging is defined as a direct touch on the AppBarLayout with movement. This 79072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * call does not affect any nested scrolling.</p> 79172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * 79272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * @return true if we are in a position to scroll the AppBarLayout via a drag, false 79372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * if not. 79472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes */ 79572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes public abstract boolean canDrag(@NonNull AppBarLayout appBarLayout); 79672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } 79772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes 798e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private int mOffsetDelta; 799a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 80050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private boolean mSkipNestedPreScroll; 80147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes private boolean mWasNestedFlung; 802631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 803c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov private ValueAnimatorCompat mOffsetAnimator; 80450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 805e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private int mOffsetToChildIndexOnLayout = INVALID_POSITION; 806e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private boolean mOffsetToChildIndexOnLayoutIsMinHeight; 807e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private float mOffsetToChildIndexOnLayoutPerc; 808e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 809d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes private WeakReference<View> mLastNestedScrollingChildRef; 81072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes private DragCallback mOnDragCallback; 811d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 812a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior() {} 813a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 814a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior(Context context, AttributeSet attrs) { 815a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 816a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 817a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 818a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 819ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, 820a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View directTargetChild, View target, int nestedScrollAxes) { 821f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes // Return true if we're nested scrolling vertically, and we have scrollable children 822f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes // and the scrolling view is big enough to scroll 823631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 824f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes && child.hasScrollableChildren() 825ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes && parent.getHeight() - directTargetChild.getHeight() <= child.getHeight(); 826631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 827c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov if (started && mOffsetAnimator != null) { 828631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Cancel any offset animation 829c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.cancel(); 830631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 831631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 832d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes // A new nested scroll has started so clear out the previous ref 833d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mLastNestedScrollingChildRef = null; 834d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 835631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return started; 836a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 837a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 838a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 839a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 840a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dx, int dy, int[] consumed) { 84150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy != 0 && !mSkipNestedPreScroll) { 84250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int min, max; 84350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy < 0) { 84450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling down 84550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getTotalScrollRange(); 84650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = min + child.getDownNestedPreScrollRange(); 84750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 84850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling up 84950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getUpNestedPreScrollRange(); 85050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = 0; 85150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 85250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes consumed[1] = scroll(coordinatorLayout, child, dy, min, max); 853a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 854a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 855a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 856a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 857a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 858a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dxConsumed, int dyConsumed, 859a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int dxUnconsumed, int dyUnconsumed) { 86050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dyUnconsumed < 0) { 86150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If the scrolling view is scrolling down but not consuming, it's probably be at 86250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // the top of it's content 86350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes scroll(coordinatorLayout, child, dyUnconsumed, 86450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes -child.getDownNestedScrollRange(), 0); 86550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Set the expanding flag so that onNestedPreScroll doesn't handle any events 86650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = true; 86750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 86850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // As we're no longer handling nested scrolls, reset the skip flag 86950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 870a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 87150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 872a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 87350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 874bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, 87550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes View target) { 87647543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes if (!mWasNestedFlung) { 877bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // If we haven't been flung then let's see if the current view has been set to snap 878bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes snapToChildIfNeeded(coordinatorLayout, abl); 879bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 880bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 881bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // Reset the flags 88250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 88347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes mWasNestedFlung = false; 884d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes // Keep a reference to the previous nested scrolling child 885d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mLastNestedScrollingChildRef = new WeakReference<>(target); 886d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 887d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 888d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes @Override 889631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public boolean onNestedFling(final CoordinatorLayout coordinatorLayout, 890631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final AppBarLayout child, View target, float velocityX, float velocityY, 891631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes boolean consumed) { 892bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes boolean flung = false; 893bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 894631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (!consumed) { 895631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // It has been consumed so let's fling ourselves 896bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes flung = fling(coordinatorLayout, child, -child.getTotalScrollRange(), 897bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 0, -velocityY); 898631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 899631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're scrolling up and the child also consumed the fling. We'll fake scroll 900c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas // up to our 'collapsed' offset 901631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (velocityY < 0) { 902631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling down 903bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes final int targetScroll = -child.getTotalScrollRange() 904631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes + child.getDownNestedPreScrollRange(); 905bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes if (getTopBottomOffsetForScrollingSibling() < targetScroll) { 906bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // If we're currently not expanded more than the target scroll, we'll 907bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // animate a fling 90857aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes animateOffsetTo(coordinatorLayout, child, targetScroll, velocityY); 909bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes flung = true; 910631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 911631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 912631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling up 913bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes final int targetScroll = -child.getUpNestedPreScrollRange(); 914bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes if (getTopBottomOffsetForScrollingSibling() > targetScroll) { 915bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // If we're currently not expanded less than the target scroll, we'll 916bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // animate a fling 91757aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes animateOffsetTo(coordinatorLayout, child, targetScroll, velocityY); 918bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes flung = true; 919631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 920631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 921631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 922631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 92347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes mWasNestedFlung = flung; 924bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes return flung; 925631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 926631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 92772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes /** 92872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * Set a callback to control any {@link AppBarLayout} dragging. 92972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * 93072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * @param callback the callback to use, or {@code null} to use the default behavior. 93172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes */ 93272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes public void setDragCallback(@Nullable DragCallback callback) { 93372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes mOnDragCallback = callback; 93472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } 93572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes 936631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private void animateOffsetTo(final CoordinatorLayout coordinatorLayout, 93757aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes final AppBarLayout child, final int offset, float velocity) { 93857aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes final int distance = Math.abs(getTopBottomOffsetForScrollingSibling() - offset); 93957aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes 94057aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes final int duration; 94157aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes velocity = Math.abs(velocity); 94257aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes if (velocity > 0) { 94357aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes duration = 3 * Math.round(1000 * (distance / velocity)); 94457aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes } else { 94557aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes final float distanceRatio = (float) distance / child.getHeight(); 94657aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes duration = (int) ((distanceRatio + 1) * 150); 94757aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes } 94857aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes 94957aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes animateOffsetWithDuration(coordinatorLayout, child, offset, duration); 95057aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes } 95157aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes 95257aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes private void animateOffsetWithDuration(final CoordinatorLayout coordinatorLayout, 95357aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes final AppBarLayout child, final int offset, final int duration) { 954dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes final int currentOffset = getTopBottomOffsetForScrollingSibling(); 955dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes if (currentOffset == offset) { 956c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov if (mOffsetAnimator != null && mOffsetAnimator.isRunning()) { 957c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.cancel(); 958dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes } 959dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes return; 960dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes } 961dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes 962c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov if (mOffsetAnimator == null) { 963c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator = ViewUtils.createAnimator(); 964c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR); 9655a0083cc8328f05b15efe0293b7aa83723cdd9adChris Banes mOffsetAnimator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() { 966631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 967631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void onAnimationUpdate(ValueAnimatorCompat animator) { 968ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor setHeaderTopBottomOffset(coordinatorLayout, child, 969631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes animator.getAnimatedIntValue()); 970631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 971631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes }); 972631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 973c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.cancel(); 974631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 975631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 97657aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes mOffsetAnimator.setDuration(Math.min(duration, MAX_OFFSET_ANIMATION_DURATION)); 977c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.setIntValues(currentOffset, offset); 978c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.start(); 979631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 980631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 981108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes private int getChildIndexOnOffset(AppBarLayout abl, final int offset) { 982bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes for (int i = 0, count = abl.getChildCount(); i < count; i++) { 983bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes View child = abl.getChildAt(i); 984bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes if (child.getTop() <= -offset && child.getBottom() >= -offset) { 985108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes return i; 986bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 987bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 988108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes return -1; 989bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 990bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 991bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes private void snapToChildIfNeeded(CoordinatorLayout coordinatorLayout, AppBarLayout abl) { 992bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes final int offset = getTopBottomOffsetForScrollingSibling(); 993108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes final int offsetChildIndex = getChildIndexOnOffset(abl, offset); 994108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes if (offsetChildIndex >= 0) { 995108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes final View offsetChild = abl.getChildAt(offsetChildIndex); 996bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes final LayoutParams lp = (LayoutParams) offsetChild.getLayoutParams(); 997108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes final int flags = lp.getScrollFlags(); 998108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes 999108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes if ((flags & LayoutParams.FLAG_SNAP) == LayoutParams.FLAG_SNAP) { 1000bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // We're set the snap, so animate the offset to the nearest edge 1001108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes int snapTop = -offsetChild.getTop(); 1002108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes int snapBottom = -offsetChild.getBottom(); 1003108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes 1004108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes if (offsetChildIndex == abl.getChildCount() - 1) { 1005108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes // If this is the last child, we need to take the top inset into account 1006108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes snapBottom += abl.getTopInset(); 1007108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes } 1008dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes 1009108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes if (checkFlag(flags, LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED)) { 1010108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes // If the view is set only exit until it is collapsed, we'll abide by that 1011108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes snapBottom += ViewCompat.getMinimumHeight(offsetChild); 1012108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes } else if (checkFlag(flags, LayoutParams.FLAG_QUICK_RETURN 1013108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes | LayoutParams.SCROLL_FLAG_ENTER_ALWAYS)) { 1014108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes // If it's set to always enter collapsed, it actually has two states. We 1015108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes // select the state and then snap within the state 1016108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes final int seam = snapBottom + ViewCompat.getMinimumHeight(offsetChild); 1017108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes if (offset < seam) { 1018108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes snapTop = seam; 1019108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes } else { 1020108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes snapBottom = seam; 1021108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes } 1022dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes } 1023dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes 1024108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes final int newOffset = offset < (snapBottom + snapTop) / 2 1025108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes ? snapBottom 1026108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes : snapTop; 1027bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes animateOffsetTo(coordinatorLayout, abl, 102857aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes MathUtils.constrain(newOffset, -abl.getTotalScrollRange(), 0), 0); 1029bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 1030bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 1031bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 1032bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 1033108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes private static boolean checkFlag(final int flags, final int check) { 1034108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes return (flags & check) == check; 1035108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes } 1036108a7a812404313b7b9f8daa154e657e49e0aae6Chris Banes 1037631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 1038bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes public boolean onMeasureChild(CoordinatorLayout parent, AppBarLayout child, 1039bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, 1040bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes int heightUsed) { 1041bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes final CoordinatorLayout.LayoutParams lp = 1042bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes (CoordinatorLayout.LayoutParams) child.getLayoutParams(); 1043bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes if (lp.height == CoordinatorLayout.LayoutParams.WRAP_CONTENT) { 1044bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes // If the view is set to wrap on it's height, CoordinatorLayout by default will 1045bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes // cap the view at the CoL's height. Since the AppBarLayout can scroll, this isn't 1046bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes // what we actually want, so we measure it ourselves with an unspecified spec to 1047bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes // allow the child to be larger than it's parent 1048bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes parent.onMeasureChild(child, parentWidthMeasureSpec, widthUsed, 1049bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightUsed); 1050bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes return true; 1051bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes } 1052bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes 1053bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes // Let the parent handle it as normal 1054bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, 1055bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes parentHeightMeasureSpec, heightUsed); 1056bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes } 1057bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes 1058bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes @Override 10599995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, 106050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int layoutDirection) { 10619995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes boolean handled = super.onLayoutChild(parent, abl, layoutDirection); 10629995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 10632bab57ed97f284a8152d180cca796739afa3a71dChris Banes // The priority for for actions here is (first which is true wins): 10642bab57ed97f284a8152d180cca796739afa3a71dChris Banes // 1. forced pending actions 10652bab57ed97f284a8152d180cca796739afa3a71dChris Banes // 2. offsets for restorations 10662bab57ed97f284a8152d180cca796739afa3a71dChris Banes // 3. non-forced pending actions 10679995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final int pendingAction = abl.getPendingAction(); 10682bab57ed97f284a8152d180cca796739afa3a71dChris Banes if (mOffsetToChildIndexOnLayout >= 0 && (pendingAction & PENDING_ACTION_FORCE) == 0) { 10692bab57ed97f284a8152d180cca796739afa3a71dChris Banes View child = abl.getChildAt(mOffsetToChildIndexOnLayout); 10702bab57ed97f284a8152d180cca796739afa3a71dChris Banes int offset = -child.getBottom(); 10712bab57ed97f284a8152d180cca796739afa3a71dChris Banes if (mOffsetToChildIndexOnLayoutIsMinHeight) { 10722bab57ed97f284a8152d180cca796739afa3a71dChris Banes offset += ViewCompat.getMinimumHeight(child) + abl.getTopInset(); 10732bab57ed97f284a8152d180cca796739afa3a71dChris Banes } else { 10742bab57ed97f284a8152d180cca796739afa3a71dChris Banes offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc); 10752bab57ed97f284a8152d180cca796739afa3a71dChris Banes } 10762bab57ed97f284a8152d180cca796739afa3a71dChris Banes setHeaderTopBottomOffset(parent, abl, offset); 10772bab57ed97f284a8152d180cca796739afa3a71dChris Banes } else if (pendingAction != PENDING_ACTION_NONE) { 10789995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final boolean animate = (pendingAction & PENDING_ACTION_ANIMATE_ENABLED) != 0; 10799995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if ((pendingAction & PENDING_ACTION_COLLAPSED) != 0) { 10809995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final int offset = -abl.getUpNestedPreScrollRange(); 10819995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (animate) { 108257aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes animateOffsetTo(parent, abl, offset, 0); 10839995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else { 1084ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor setHeaderTopBottomOffset(parent, abl, offset); 10859995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 10869995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else if ((pendingAction & PENDING_ACTION_EXPANDED) != 0) { 10879995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (animate) { 108857aa00c058f3278fccc7a23f3cef29142bb14dbbChris Banes animateOffsetTo(parent, abl, 0, 0); 10899995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else { 1090ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor setHeaderTopBottomOffset(parent, abl, 0); 10919995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 10929995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 1093e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 109450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 109579aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes // Finally reset any pending states 109679aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes abl.resetPendingAction(); 109779aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes mOffsetToChildIndexOnLayout = INVALID_POSITION; 109879aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes 109947543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes // We may have changed size, so let's constrain the top and bottom offset correctly, 110047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes // just in case we're out of the bounds 110147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes setTopAndBottomOffset( 110247543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes MathUtils.constrain(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0)); 110347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes 1104df2d45b76203a9c4101629005eb27059d7f80297Chris Banes // Update the AppBarLayout's drawable state for any elevation changes. 1105df2d45b76203a9c4101629005eb27059d7f80297Chris Banes // This is needed so that the elevation is set in the first layout, so that 1106df2d45b76203a9c4101629005eb27059d7f80297Chris Banes // we don't get a visual elevation jump pre-N (due to the draw dispatch skip) 1107df2d45b76203a9c4101629005eb27059d7f80297Chris Banes updateAppBarLayoutDrawableState(parent, abl, getTopAndBottomOffset(), 0, true); 1108df2d45b76203a9c4101629005eb27059d7f80297Chris Banes 11091a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // Make sure we dispatch the offset update 11101a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes abl.dispatchOffsetUpdates(getTopAndBottomOffset()); 111150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 111250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return handled; 1113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 111572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes @Override 111672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes boolean canDragView(AppBarLayout view) { 111772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes if (mOnDragCallback != null) { 111872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes // If there is a drag callback set, it's in control 111972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes return mOnDragCallback.canDrag(view); 112072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } 112172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes 112272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes // Else we'll use the default behaviour of seeing if it can scroll down 1123d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (mLastNestedScrollingChildRef != null) { 112472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes // If we have a reference to a scrolling view, check it 112572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes final View scrollingView = mLastNestedScrollingChildRef.get(); 112672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes return scrollingView != null && scrollingView.isShown() 112772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes && !ViewCompat.canScrollVertically(scrollingView, -1); 112872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } else { 112972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes // Otherwise we assume that the scrolling view hasn't been scrolled and can drag. 113072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes return true; 1131d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 1132d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 1133d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 1134ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor @Override 113547543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes void onFlingFinished(CoordinatorLayout parent, AppBarLayout layout) { 113647543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes // At the end of a manual fling, check to see if we need to snap to the edge-child 113747543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes snapToChildIfNeeded(parent, layout); 113847543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes } 113947543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes 114047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes @Override 114172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes int getMaxDragOffset(AppBarLayout view) { 114272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes return -view.getDownNestedScrollRange(); 114372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } 114472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes 114572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes @Override 114672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes int getScrollRangeForDragFling(AppBarLayout view) { 114772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes return view.getTotalScrollRange(); 114872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } 114972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes 115072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes @Override 115172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes int setHeaderTopBottomOffset(CoordinatorLayout coordinatorLayout, 1152bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) { 1153e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int curOffset = getTopBottomOffsetForScrollingSibling(); 1154a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int consumed = 0; 1155a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 11565e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) { 1157631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we have some scrolling range, and we're currently within the min and max 1158631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // offsets, calculate a new offset 115950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset); 1160a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (curOffset != newOffset) { 1161e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int interpolatedOffset = appBarLayout.hasChildWithInterpolator() 1162e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ? interpolateOffset(appBarLayout, newOffset) 1163e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes : newOffset; 1164e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 11651a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset); 1166631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 1167a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update how much dy we have consumed 1168a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes consumed = curOffset - newOffset; 1169a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update the stored sibling offset 1170e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetDelta = newOffset - interpolatedOffset; 1171a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1172a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) { 1173a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the offset hasn't changed and we're using an interpolated scroll 1174a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // then we need to keep any dependent views updated. CoL will do this for 1175a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us when we move, but we need to do it manually when we don't (as an 1176a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // interpolated scroll may finish early). 1177a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes coordinatorLayout.dispatchDependentViewsChanged(appBarLayout); 1178a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1179a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1180631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Dispatch the updates to any listeners 11811a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes appBarLayout.dispatchOffsetUpdates(getTopAndBottomOffset()); 11821a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 11831a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // Update the AppBarLayout's drawable state (for any elevation changes) 1184c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes updateAppBarLayoutDrawableState(coordinatorLayout, appBarLayout, newOffset, 1185df2d45b76203a9c4101629005eb27059d7f80297Chris Banes newOffset < curOffset ? -1 : 1, false); 1186a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 11875e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes } else { 11885e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes // Reset the offset delta 11895e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes mOffsetDelta = 0; 1190a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1191a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1192a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return consumed; 1193a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1194a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1195bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes @VisibleForTesting 1196bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes boolean isOffsetAnimatorRunning() { 1197bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes return mOffsetAnimator != null && mOffsetAnimator.isRunning(); 1198bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes } 1199bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes 1200a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int interpolateOffset(AppBarLayout layout, final int offset) { 1201a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int absOffset = Math.abs(offset); 1202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 1204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = layout.getChildAt(i); 1205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 120650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 1207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 120850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (absOffset >= child.getTop() && absOffset <= child.getBottom()) { 1209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 121050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int childScrollableHeight = 0; 121150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int flags = childLp.getScrollFlags(); 121250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 1213b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // We're set to scroll so add the child's height plus margin 1214b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes childScrollableHeight += child.getHeight() + childLp.topMargin 1215b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes + childLp.bottomMargin; 1216b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes 121750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 1218b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // For a collapsing scroll, we to take the collapsed height 1219b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // into account. 122050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight -= ViewCompat.getMinimumHeight(child); 122150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 122250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 122350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 1224ea004a01f44aaf36212120c936952c1742d03d30Chris Banes if (ViewCompat.getFitsSystemWindows(child)) { 1225ea004a01f44aaf36212120c936952c1742d03d30Chris Banes childScrollableHeight -= layout.getTopInset(); 1226ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1227ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 122850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childScrollableHeight > 0) { 122950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int offsetForView = absOffset - child.getTop(); 123050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int interpolatedDiff = Math.round(childScrollableHeight * 123150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes interpolator.getInterpolation( 123250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes offsetForView / (float) childScrollableHeight)); 123350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 123450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return Integer.signum(offset) * (child.getTop() + interpolatedDiff); 123550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 1236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 123750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 123850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If we get to here then the view on the offset isn't suitable for interpolated 123950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // scrolling. So break out of the loop 124050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes break; 1241a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1242a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1243a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1244a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return offset; 1245a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1247c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes private void updateAppBarLayoutDrawableState(final CoordinatorLayout parent, 1248df2d45b76203a9c4101629005eb27059d7f80297Chris Banes final AppBarLayout layout, final int offset, final int direction, 1249df2d45b76203a9c4101629005eb27059d7f80297Chris Banes final boolean forceJump) { 12501a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final View child = getAppBarChildOnOffset(layout, offset); 12511a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (child != null) { 12521a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 12531a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final int flags = childLp.getScrollFlags(); 12541a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes boolean collapsed = false; 12551a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 12561a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 12571a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final int minHeight = ViewCompat.getMinimumHeight(child); 12581a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 12591a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (direction > 0 && (flags & (LayoutParams.SCROLL_FLAG_ENTER_ALWAYS 12601a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes | LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED)) != 0) { 12611a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // We're set to enter always collapsed so we are only collapsed when 12621a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // being scrolled down, and in a collapsed offset 12631a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset(); 12641a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 12651a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // We're set to exit until collapsed, so any offset which results in 12661a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // the minimum height (or less) being shown is collapsed 12671a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset(); 12681a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 12691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 12701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 1271c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes final boolean changed = layout.setCollapsedState(collapsed); 1272c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes 1273df2d45b76203a9c4101629005eb27059d7f80297Chris Banes if (Build.VERSION.SDK_INT >= 11 && (forceJump 1274df2d45b76203a9c4101629005eb27059d7f80297Chris Banes || (changed && shouldJumpElevationState(parent, layout)))) { 1275c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes // If the collapsed state changed, we may need to 1276c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes // jump to the current state if we have an overlapping view 1277c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes layout.jumpDrawablesToCurrentState(); 1278c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes } 1279c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes } 1280c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes } 1281c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes 1282c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes private boolean shouldJumpElevationState(CoordinatorLayout parent, AppBarLayout layout) { 1283c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes // We should jump the elevated state if we have a dependent scrolling view which has 1284c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes // an overlapping top (i.e. overlaps us) 1285c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes final List<View> dependencies = parent.getDependents(layout); 1286c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes for (int i = 0, size = dependencies.size(); i < size; i++) { 1287c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes final View dependency = dependencies.get(i); 1288c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes final CoordinatorLayout.LayoutParams lp = 1289c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes (CoordinatorLayout.LayoutParams) dependency.getLayoutParams(); 1290c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes final CoordinatorLayout.Behavior behavior = lp.getBehavior(); 1291c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes 1292c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes if (behavior instanceof ScrollingViewBehavior) { 1293c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes return ((ScrollingViewBehavior) behavior).getOverlayTop() != 0; 1294c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes } 12951a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 1296c07c8e1e74a026c0d9e3a9dd119913436380be38Chris Banes return false; 12971a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 12981a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 12991a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private static View getAppBarChildOnOffset(final AppBarLayout layout, final int offset) { 13001a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final int absOffset = Math.abs(offset); 13011a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 13021a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final View child = layout.getChildAt(i); 13031a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (absOffset >= child.getTop() && absOffset <= child.getBottom()) { 13041a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes return child; 13051a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 13061a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 13071a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes return null; 13081a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 13091a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 1310ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor @Override 131172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes int getTopBottomOffsetForScrollingSibling() { 1312e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return getTopAndBottomOffset() + mOffsetDelta; 1313e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1314e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1315e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1316824644fc70d5fd36bcd4c1e155c1279aebcac007Chris Banes public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout abl) { 1317824644fc70d5fd36bcd4c1e155c1279aebcac007Chris Banes final Parcelable superState = super.onSaveInstanceState(parent, abl); 1318e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int offset = getTopAndBottomOffset(); 1319e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1320e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes // Try and find the first visible child... 1321824644fc70d5fd36bcd4c1e155c1279aebcac007Chris Banes for (int i = 0, count = abl.getChildCount(); i < count; i++) { 1322824644fc70d5fd36bcd4c1e155c1279aebcac007Chris Banes View child = abl.getChildAt(i); 1323e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int visBottom = child.getBottom() + offset; 1324e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1325e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (child.getTop() + offset <= 0 && visBottom >= 0) { 1326e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final SavedState ss = new SavedState(superState); 1327e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibleChildIndex = i; 1328c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas ss.firstVisibleChildAtMinimumHeight = 1329824644fc70d5fd36bcd4c1e155c1279aebcac007Chris Banes visBottom == (ViewCompat.getMinimumHeight(child) + abl.getTopInset()); 1330c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas ss.firstVisibleChildPercentageShown = visBottom / (float) child.getHeight(); 1331e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return ss; 1332e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1333e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1334e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1335e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes // Else we'll just return the super state 1336e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return superState; 1337e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1338e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1339e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1340e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout, 1341e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes Parcelable state) { 1342e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (state instanceof SavedState) { 1343e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final SavedState ss = (SavedState) state; 1344e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.onRestoreInstanceState(parent, appBarLayout, ss.getSuperState()); 1345e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = ss.firstVisibleChildIndex; 1346c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas mOffsetToChildIndexOnLayoutPerc = ss.firstVisibleChildPercentageShown; 1347c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas mOffsetToChildIndexOnLayoutIsMinHeight = ss.firstVisibleChildAtMinimumHeight; 1348e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } else { 1349e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.onRestoreInstanceState(parent, appBarLayout, state); 1350e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = INVALID_POSITION; 1351e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1352e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1353e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 135405f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes protected static class SavedState extends AbsSavedState { 1355e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes int firstVisibleChildIndex; 1356c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas float firstVisibleChildPercentageShown; 1357c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas boolean firstVisibleChildAtMinimumHeight; 1358e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 135962ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes public SavedState(Parcel source, ClassLoader loader) { 136005f5ba020fa6caa658c75b6d77436aa980ca0fccChris Banes super(source, loader); 1361e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibleChildIndex = source.readInt(); 1362c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas firstVisibleChildPercentageShown = source.readFloat(); 1363c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas firstVisibleChildAtMinimumHeight = source.readByte() != 0; 1364e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1365e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1366e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState(Parcelable superState) { 1367e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super(superState); 1368e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1369e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1370e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1371e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public void writeToParcel(Parcel dest, int flags) { 1372e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.writeToParcel(dest, flags); 1373e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeInt(firstVisibleChildIndex); 1374c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas dest.writeFloat(firstVisibleChildPercentageShown); 1375c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas dest.writeByte((byte) (firstVisibleChildAtMinimumHeight ? 1 : 0)); 1376e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1377e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1378e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public static final Parcelable.Creator<SavedState> CREATOR = 137962ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() { 1380e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 138162ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes public SavedState createFromParcel(Parcel source, ClassLoader loader) { 138262ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes return new SavedState(source, loader); 1383e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1384e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1385e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1386e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState[] newArray(int size) { 1387e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return new SavedState[size]; 1388e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 138962ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes }); 1390a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1391a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1392a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1393a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 1394a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Behavior which should be used by {@link View}s which can scroll vertically and support 1395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * nested scrolling to automatically scroll any {@link AppBarLayout} siblings. 1396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 1397ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor public static class ScrollingViewBehavior extends HeaderScrollingViewBehavior { 1398a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1399a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior() {} 1400a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior(Context context, AttributeSet attrs) { 1402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 1403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1404b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes final TypedArray a = context.obtainStyledAttributes(attrs, 140573e7a06fb66110495fa4e98a91831a47feab2526Chris Banes R.styleable.ScrollingViewBehavior_Layout); 1406b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes setOverlayTop(a.getDimensionPixelSize( 140773e7a06fb66110495fa4e98a91831a47feab2526Chris Banes R.styleable.ScrollingViewBehavior_Layout_behavior_overlapTop, 0)); 1408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 1409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { 1413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We depend on any AppBarLayouts 1414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return dependency instanceof AppBarLayout; 1415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1417a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onDependentViewChanged(CoordinatorLayout parent, View child, 1419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View dependency) { 14205e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes offsetChildAsNeeded(parent, child, dependency); 14210d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes return false; 14220d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes } 14230d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes 142400a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes @Override 14254cf1d92509224fab3ca69b419a005c536ab75c5cChris Banes public boolean onRequestChildRectangleOnScreen(CoordinatorLayout parent, View child, 142600a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes Rect rectangle, boolean immediate) { 142700a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes final AppBarLayout header = findFirstDependency(parent.getDependencies(child)); 142800a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes if (header != null) { 142900a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes // Offset the rect by the child's left/top 143000a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes rectangle.offset(child.getLeft(), child.getTop()); 143100a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes 143200a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes final Rect parentRect = mTempRect1; 143300a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes parentRect.set(0, 0, parent.getWidth(), parent.getHeight()); 143400a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes 143500a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes if (!parentRect.contains(rectangle)) { 143600a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes // If the rectangle can not be fully seen the visible bounds, collapse 143700a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes // the AppBarLayout 143800a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes header.setExpanded(false, !immediate); 143900a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes return true; 144000a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes } 144100a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes } 144200a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes return false; 144300a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes } 144400a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes 14455e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes private void offsetChildAsNeeded(CoordinatorLayout parent, View child, View dependency) { 1446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final CoordinatorLayout.Behavior behavior = 1447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); 1448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (behavior instanceof Behavior) { 14495e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes // Offset the child, pinning it to the bottom the header-dependency, maintaining 1450bb3740d273d4b5108ab63f6d264466250a197e5bChris Banes // any vertical gap and overlap 14515e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes final Behavior ablBehavior = (Behavior) behavior; 1452318baf84dade07174d71e10322e3b10ab4b0c28cChris Banes ViewCompat.offsetTopAndBottom(child, (dependency.getBottom() - child.getTop()) 14535e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes + ablBehavior.mOffsetDelta 14545e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes + getVerticalLayoutGap() 1455b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes - getOverlapPixelsForOffset(dependency)); 1456ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1457ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1458ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 1459b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes @Override 1460b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes float getOverlapRatioForOffset(final View header) { 1461b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes if (header instanceof AppBarLayout) { 1462b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes final AppBarLayout abl = (AppBarLayout) header; 1463ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int totalScrollRange = abl.getTotalScrollRange(); 1464ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int preScrollDown = abl.getDownNestedPreScrollRange(); 1465b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes final int offset = getAppBarLayoutOffset(abl); 1466a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1467ea004a01f44aaf36212120c936952c1742d03d30Chris Banes if (preScrollDown != 0 && (totalScrollRange + offset) <= preScrollDown) { 1468ea004a01f44aaf36212120c936952c1742d03d30Chris Banes // If we're in a pre-scroll down. Don't use the offset at all. 1469ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return 0; 1470a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 1471ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int availScrollRange = totalScrollRange - preScrollDown; 1472dfb857dd12b359c0bdae11a23e039f286d54dc0aChris Banes if (availScrollRange != 0) { 1473ea004a01f44aaf36212120c936952c1742d03d30Chris Banes // Else we'll use a interpolated ratio of the overlap, depending on offset 1474b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes return 1f + (offset / (float) availScrollRange); 1475ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1476a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1477a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1478b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes return 0f; 1479a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1480a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1481b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes private static int getAppBarLayoutOffset(AppBarLayout abl) { 1482b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes final CoordinatorLayout.Behavior behavior = 1483b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes ((CoordinatorLayout.LayoutParams) abl.getLayoutParams()).getBehavior(); 1484b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes if (behavior instanceof Behavior) { 1485b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes return ((Behavior) behavior).getTopBottomOffsetForScrollingSibling(); 1486b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes } 1487b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes return 0; 1488a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1489a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1490ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor @Override 149100a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes AppBarLayout findFirstDependency(List<View> views) { 1492a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = views.size(); i < z; i++) { 1493a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View view = views.get(i); 1494a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (view instanceof AppBarLayout) { 149500a00a7d3ba8279294f63994473afc32e05dcf10Chris Banes return (AppBarLayout) view; 1496a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1497a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1498a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return null; 1499a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1500ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor 1501ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor @Override 150272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes int getScrollRange(View v) { 1503ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor if (v instanceof AppBarLayout) { 1504ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor return ((AppBarLayout) v).getTotalScrollRange(); 1505ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor } else { 1506ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor return super.getScrollRange(v); 1507ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor } 1508ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor } 1509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes} 1511