AppBarLayout.java revision b75a16fb6433ab27973d419d23ed1286c073fca8
1a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/* 2a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Copyright (C) 2015 The Android Open Source Project 3a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 4a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * you may not use this file except in compliance with the License. 6a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * You may obtain a copy of the License at 7a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 8a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * http://www.apache.org/licenses/LICENSE-2.0 9a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 10a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Unless required by applicable law or agreed to in writing, software 11a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * See the License for the specific language governing permissions and 14a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * limitations under the License. 15a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 16a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 17a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespackage android.support.design.widget; 18a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 19a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.Context; 20a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.res.TypedArray; 211a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banesimport android.os.Build; 22e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcel; 23e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcelable; 2450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport android.support.annotation.IntDef; 2572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.annotation.NonNull; 2672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banesimport android.support.annotation.Nullable; 27a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.design.R; 2862ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banesimport android.support.v4.os.ParcelableCompat; 2962ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banesimport android.support.v4.os.ParcelableCompatCreatorCallbacks; 30a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat; 316ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banesimport android.support.v4.view.WindowInsetsCompat; 32a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet; 33a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View; 34a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup; 35a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator; 36a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout; 37a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 3850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.Retention; 3950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.RetentionPolicy; 40d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banesimport java.lang.ref.WeakReference; 41631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.ArrayList; 42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List; 43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/** 45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of 469fb154338a62edc2c57dc036895199d6f1769400Chris Banes * material designs app bar concept, namely scrolling gestures. 47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through 49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute: 50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}. 51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}. 54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will 55a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work. 56a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 579fb154338a62edc2c57dc036895199d6f1769400Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to know when to scroll. 589fb154338a62edc2c57dc036895199d6f1769400Chris Banes * The binding is done through the {@link ScrollingViewBehavior} behavior class, meaning that you 599fb154338a62edc2c57dc036895199d6f1769400Chris Banes * should set your scrolling view's behavior to be an instance of {@link ScrollingViewBehavior}. 609fb154338a62edc2c57dc036895199d6f1769400Chris Banes * A string resource containing the full class name is available. 61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre> 63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.CoordinatorLayout 64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:android="http://schemas.android.com/apk/res/android" 65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:app="http://schemas.android.com/apk/res-auto" 66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent"> 68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v4.widget.NestedScrollView 70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent" 72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_behavior="@string/appbar_scrolling_view_behavior"> 73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <!-- Your scrolling content --> 75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.v4.widget.NestedScrollView> 77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.AppBarLayout 79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="wrap_content" 80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent"> 81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v7.widget.Toolbar 83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.TabLayout 87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.AppBarLayout> 91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.CoordinatorLayout> 93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre> 94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar"> 96a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a> 97a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class) 99a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout { 100a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1019995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_NONE = 0x0; 1029995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_EXPANDED = 0x1; 1039995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_COLLAPSED = 0x2; 1049995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4; 1059995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 107631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical 108631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * offset changes. 109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 110631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public interface OnOffsetChangedListener { 111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows 113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * child views to implement custom behavior based on the offset (for instance pinning a 114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view at a certain y value). 115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 116631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param appBarLayout the {@link AppBarLayout} which offset has changed 11750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px 118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 119631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset); 120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 122a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static final int INVALID_SCROLL_RANGE = -1; 123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mTotalScrollRange = INVALID_SCROLL_RANGE; 125a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownPreScrollRange = INVALID_SCROLL_RANGE; 126a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownScrollRange = INVALID_SCROLL_RANGE; 127a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1281a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private boolean mHaveChildWithInterpolator; 12950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 1309995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private int mPendingAction = PENDING_ACTION_NONE; 1319995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 1326ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes private WindowInsetsCompat mLastInsets; 1336ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 1341a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private List<OnOffsetChangedListener> mListeners; 1351a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 1361a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private boolean mCollapsible; 1371a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private boolean mCollapsed; 1381a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 1391a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private final int[] mTmpStatesArray = new int[2]; 140631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 141a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context) { 142a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes this(context, null); 143a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 144a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 145a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context, AttributeSet attrs) { 146a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 147a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setOrientation(VERTICAL); 14850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 149809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes ThemeUtils.checkAppCompatTheme(context); 150809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes 1511a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (Build.VERSION.SDK_INT >= 21) { 1521a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // Use the bounds view outline provider so that we cast a shadow, even without a 1531a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // background 1541a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes ViewUtilsLollipop.setBoundsViewOutlineProvider(this); 1551a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 1561a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // If we're running on API 21+, we should reset any state list animator from our 1571a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // default style 1581a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes ViewUtilsLollipop.setStateListAnimatorFromAttrs(this, attrs, 0, 1591a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes R.style.Widget_Design_AppBarLayout); 1601a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 1611a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 1621a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout, 16350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 0, R.style.Widget_Design_AppBarLayout); 16481520564f3dd783136e025174021ba4eabd6ff3cChris Banes setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background)); 1659995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (a.hasValue(R.styleable.AppBarLayout_expanded)) { 1669995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false)); 1679995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 1681a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (Build.VERSION.SDK_INT >= 21 && a.hasValue(R.styleable.AppBarLayout_elevation)) { 1691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator( 1701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes this, a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0)); 1711a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 17250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes a.recycle(); 17350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 1746ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes ViewCompat.setOnApplyWindowInsetsListener(this, 1756ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes new android.support.v4.view.OnApplyWindowInsetsListener() { 1766ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes @Override 1776ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes public WindowInsetsCompat onApplyWindowInsets(View v, 1786ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes WindowInsetsCompat insets) { 1798818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes return onWindowInsetChanged(insets); 1806ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 1816ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes }); 182631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 183631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 184631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 185631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Add a listener that will be called when the offset of this {@link AppBarLayout} changes. 186631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 187631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener The listener that will be called when the offset changes.] 188631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 189631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @see #removeOnOffsetChangedListener(OnOffsetChangedListener) 190631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 191631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void addOnOffsetChangedListener(OnOffsetChangedListener listener) { 1921a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (mListeners == null) { 1931a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes mListeners = new ArrayList<>(); 1941a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 1956f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes if (listener != null && !mListeners.contains(listener)) { 1966f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes mListeners.add(listener); 197631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 198631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 199631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 200631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 201631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Remove the previously added {@link OnOffsetChangedListener}. 202631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 203631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener the listener to remove. 204631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 205631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) { 2061a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (mListeners != null && listener != null) { 2076f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes mListeners.remove(listener); 208631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 212ea004a01f44aaf36212120c936952c1742d03d30Chris Banes protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 213ea004a01f44aaf36212120c936952c1742d03d30Chris Banes super.onMeasure(widthMeasureSpec, heightMeasureSpec); 214ea004a01f44aaf36212120c936952c1742d03d30Chris Banes invalidateScrollRanges(); 215ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 216ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 217ea004a01f44aaf36212120c936952c1742d03d30Chris Banes @Override 218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected void onLayout(boolean changed, int l, int t, int r, int b) { 219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.onLayout(changed, l, t, r, b); 22079aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes invalidateScrollRanges(); 221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = false; 223a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 224a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = true; 230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 2331a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 2341a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes updateCollapsible(); 2351a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 2361a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 2371a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private void updateCollapsible() { 2381a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes boolean haveCollapsibleChild = false; 2391a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 2401a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (((LayoutParams) getChildAt(i).getLayoutParams()).isCollapsible()) { 2411a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes haveCollapsibleChild = true; 2421a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes break; 2431a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 2441a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 2451a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes setCollapsible(haveCollapsibleChild); 246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 248ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private void invalidateScrollRanges() { 249ea004a01f44aaf36212120c936952c1742d03d30Chris Banes // Invalidate the scroll ranges 250ea004a01f44aaf36212120c936952c1742d03d30Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 251ea004a01f44aaf36212120c936952c1742d03d30Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 252ea004a01f44aaf36212120c936952c1742d03d30Chris Banes mDownScrollRange = INVALID_SCROLL_RANGE; 253ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 254ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 255a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 256a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOrientation(int orientation) { 257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (orientation != VERTICAL) { 258a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes throw new IllegalArgumentException("AppBarLayout is always vertical and does" 259a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes + " not support horizontal orientation"); 260a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.setOrientation(orientation); 262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 2649995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes /** 2659995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * Sets whether this {@link AppBarLayout} is expanded or not, animating if it has already 2669995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * been laid out. 2679995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2689995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a 2699995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * direct child of a {@link CoordinatorLayout}.</p> 2709995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2719995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param expanded true if the layout should be fully expanded, false if it should 2729995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * be fully collapsed 2739995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2749995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_expanded 2759995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes */ 2769995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public void setExpanded(boolean expanded) { 2779995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes setExpanded(expanded, ViewCompat.isLaidOut(this)); 2789995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 2799995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 2809995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes /** 2819995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * Sets whether this {@link AppBarLayout} is expanded or not. 2829995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2839995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a 2849995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * direct child of a {@link CoordinatorLayout}.</p> 2859995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2869995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param expanded true if the layout should be fully expanded, false if it should 2879995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * be fully collapsed 2889995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param animate Whether to animate to the new state 2899995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2909995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_expanded 2919995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes */ 2929995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public void setExpanded(boolean expanded, boolean animate) { 2939995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mPendingAction = (expanded ? PENDING_ACTION_EXPANDED : PENDING_ACTION_COLLAPSED) 2949995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes | (animate ? PENDING_ACTION_ANIMATE_ENABLED : 0); 2959995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes requestLayout(); 2969995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 2979995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return p instanceof LayoutParams; 301a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateDefaultLayoutParams() { 305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams generateLayoutParams(AttributeSet attrs) { 310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(getContext(), attrs); 311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (p instanceof LinearLayout.LayoutParams) { 316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((LinearLayout.LayoutParams) p); 317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (p instanceof MarginLayoutParams) { 318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((MarginLayoutParams) p); 319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(p); 321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 323ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private boolean hasChildWithInterpolator() { 324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mHaveChildWithInterpolator; 325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 3286ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes * Returns the scroll range of all children. 329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @return the scroll range in px 331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 3326ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes public final int getTotalScrollRange() { 333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mTotalScrollRange != INVALID_SCROLL_RANGE) { 334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange; 335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 341ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int childHeight = child.getMeasuredHeight(); 342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 346b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes range += childHeight + lp.topMargin + lp.bottomMargin; 347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 3506ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // We also break straight away since later views can't scroll beneath 351a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us 3526ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes range -= ViewCompat.getMinimumHeight(child); 3536ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes break; 354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 3614832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes return mTotalScrollRange = Math.max(0, range - getTopInset()); 362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 364ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private boolean hasScrollableChildren() { 365a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return getTotalScrollRange() != 0; 366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 368a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 369a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling up from a nested pre-scroll. 370a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 371ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private int getUpNestedPreScrollRange() { 37250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return getTotalScrollRange(); 373a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested pre-scroll. 377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 378ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private int getDownNestedPreScrollRange() { 379a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownPreScrollRange != INVALID_SCROLL_RANGE) { 380a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange; 382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 384a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 385a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 386a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 387a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 388ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int childHeight = child.getMeasuredHeight(); 389a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 390a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 391a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) { 392b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // First take the margin into account 393b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes range += lp.topMargin + lp.bottomMargin; 394a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // The view has the quick return flag combination... 395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) { 396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If they're set to enter collapsed, use the minimum height 397a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += ViewCompat.getMinimumHeight(child); 39847543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes } else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 39947543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes // Only enter by the amount of the collapsed height 40047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes range += childHeight - ViewCompat.getMinimumHeight(child); 401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Else use the full height 403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 404a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 405a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (range > 0) { 406a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we've hit an non-quick return scrollable view, and we've already hit a 407a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // quick return view, return now 408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 411090293b68e5aad0af0a0d8ae079ac0df743ff3ecChris Banes return mDownPreScrollRange = Math.max(0, range); 412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested scroll. 416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 417ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private int getDownNestedScrollRange() { 418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownScrollRange != INVALID_SCROLL_RANGE) { 419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 420a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange; 421a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 422a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 423a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 424c1ce4f6cf4d99d6856c4259bf1bb1cb56f604737Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 425a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 426a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 427ea004a01f44aaf36212120c936952c1742d03d30Chris Banes int childHeight = child.getMeasuredHeight(); 428b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes childHeight += lp.topMargin + lp.bottomMargin; 429a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 430a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 431a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 432a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 433a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 434a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 435a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 436a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 437a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing exit scroll, we to take the collapsed height into account. 4384832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes // We also break the range straight away since later views can't scroll 439a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // beneath us 4404832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes range -= ViewCompat.getMinimumHeight(child) + getTopInset(); 4414832063daa6620f0ad51c4a16f31502dab1adc89Chris Banes break; 442a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 443a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 444a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 4494c33be829067714342b629d29329206bc2116afeChris Banes return mDownScrollRange = Math.max(0, range); 450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 451a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 4521a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private void dispatchOffsetUpdates(int offset) { 4531a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // Iterate backwards through the list so that most recently added listeners 4541a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // get the first chance to decide 4551a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (mListeners != null) { 4561a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes for (int i = 0, z = mListeners.size(); i < z; i++) { 4571a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final OnOffsetChangedListener listener = mListeners.get(i); 4581a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (listener != null) { 4591a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes listener.onOffsetChanged(this, offset); 4601a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 4611a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 4621a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 4631a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 4641a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 46550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int getMinimumHeightForVisibleOverlappingContent() { 46617ed3263761329f6aa6796941358c41001fff325Chris Banes final int topInset = getTopInset(); 46750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int minHeight = ViewCompat.getMinimumHeight(this); 46850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (minHeight != 0) { 46950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If this layout has a min height, use it (doubled) 4706ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes return (minHeight * 2) + topInset; 47150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 47250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 47350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Otherwise, we'll use twice the min height of our last child 47450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childCount = getChildCount(); 475b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes final int lastChildMinHeight = childCount >= 1 476b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) : 0; 477b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes if (lastChildMinHeight != 0) { 478b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes return (lastChildMinHeight * 2) + topInset; 479b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes } 480b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes 481b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes // If we reach here then we don't have a min height explicitly set. Instead we'll take a 482b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes // guess at 1/3 of our height being visible 483b75a16fb6433ab27973d419d23ed1286c073fca8Chris Banes return getHeight() / 3; 48450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 48550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 4861a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes @Override 4871a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes protected int[] onCreateDrawableState(int extraSpace) { 4881a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final int[] extraStates = mTmpStatesArray; 4891a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final int[] states = super.onCreateDrawableState(extraSpace + extraStates.length); 4901a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 4911a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes extraStates[0] = mCollapsible ? R.attr.state_collapsible : -R.attr.state_collapsible; 4921a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes extraStates[1] = mCollapsible && mCollapsed 4931a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes ? R.attr.state_collapsed : -R.attr.state_collapsed; 4941a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 4951a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes return mergeDrawableStates(states, extraStates); 4961a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 4971a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 4981a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private void setCollapsible(boolean collapsible) { 4991a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (mCollapsible != collapsible) { 5001a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes mCollapsible = collapsible; 5011a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes refreshDrawableState(); 5021a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 5031a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 5041a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 5051a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private void setCollapsed(boolean collapsed) { 5061a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (mCollapsed != collapsed) { 5071a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes mCollapsed = collapsed; 5081a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes refreshDrawableState(); 5091a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 5101a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 5111a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 51250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** 5131a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes * @deprecated target elevation is now deprecated. AppBarLayout's elevation is now 514926cd28257536b1206934d69585a2dab100147d5Chris Banes * controlled via a {@link android.animation.StateListAnimator}. If a target 515926cd28257536b1206934d69585a2dab100147d5Chris Banes * elevation is set, either by this method or the {@code app:elevation} attibute, 516926cd28257536b1206934d69585a2dab100147d5Chris Banes * a new state list animator is created which uses the given {@code elevation} value. 517631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 5181a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_elevation 51950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes */ 5201a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes @Deprecated 521631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void setTargetElevation(float elevation) { 5221a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (Build.VERSION.SDK_INT >= 21) { 5231a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(this, elevation); 5241a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 525631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 526631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 527631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 5281a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes * @deprecated target elevation is now deprecated. AppBarLayout's elevation is now 529926cd28257536b1206934d69585a2dab100147d5Chris Banes * controlled via a {@link android.animation.StateListAnimator}. This method now 530926cd28257536b1206934d69585a2dab100147d5Chris Banes * always returns 0. 531631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 5321a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes @Deprecated 533631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public float getTargetElevation() { 5341a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes return 0; 53550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 53650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 537ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private int getPendingAction() { 5389995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes return mPendingAction; 5399995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 5409995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 541ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private void resetPendingAction() { 5429995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mPendingAction = PENDING_ACTION_NONE; 5439995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 5449995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 545ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private int getTopInset() { 546ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; 547ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 548ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 5498818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes private WindowInsetsCompat onWindowInsetChanged(final WindowInsetsCompat insets) { 5508818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes WindowInsetsCompat newInsets = null; 5518818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes 5528818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes if (ViewCompat.getFitsSystemWindows(this)) { 5538818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes // If we're set to fit system windows, keep the insets 5548818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes newInsets = insets; 5558818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes } 5568818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes 5578818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes // If our insets have changed, keep them and invalidate the scroll ranges... 5588818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes if (newInsets != mLastInsets) { 5598818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes mLastInsets = newInsets; 5608818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes invalidateScrollRanges(); 5618818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes } 5628818e8e12682e5df1535cd9b2d792494bd3e059dChris Banes 56317ed3263761329f6aa6796941358c41001fff325Chris Banes return insets; 5646ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 5656ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class LayoutParams extends LinearLayout.LayoutParams { 56750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 56850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** @hide */ 56950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @IntDef(flag=true, value={ 57050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_SCROLL, 57150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_EXIT_UNTIL_COLLAPSED, 57250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS, 573bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED, 574bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes SCROLL_FLAG_SNAP 57550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes }) 57650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Retention(RetentionPolicy.SOURCE) 57750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public @interface ScrollFlags {} 57850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 579a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 580a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The view will be scroll in direct relation to scroll events. This flag needs to be 581a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * set for any of the other flags to take effect. If any sibling views 582a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * before this one do not have this flag, then this value has no effect. 583a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 584a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_SCROLL = 0x1; 585a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 586a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 587a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When exiting (scrolling off screen) the view will be scrolled until it is 588a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 'collapsed'. The collapsed height is defined by the view's minimum height. 589a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 590a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 591a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 592a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 593a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2; 594a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 596a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When entering (scrolling on screen) the view will scroll on any downwards 597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll event, regardless of whether the scrolling view is also scrolling. This 598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * is commonly referred to as the 'quick return' pattern. 599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 600a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4; 601a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 602a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 603a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * An additional flag for 'enterAlways' which modifies the returning view to 604a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * only initially scroll back to it's collapsed height. Once the scrolling view has 605a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * reached the end of it's scroll range, the remainder of this view will be scrolled 606a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * into view. The collapsed height is defined by the view's minimum height. 607a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 608a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 609a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 610a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 611a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8; 612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 613a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 614bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * Upon a scroll ending, if the view is only partially visible then it will be snapped 615bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * and scrolled to it's closest edge. For example, if the view only has it's bottom 25% 616bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * displayed, it will be scrolled off screen completely. Conversely, if it's bottom 75% 617bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * is visible then it will be scrolled fully into view. 618bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes */ 619bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes public static final int SCROLL_FLAG_SNAP = 0x10; 620bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 621bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes /** 622bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * Internal flags which allows quick checking features 623a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 624a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS; 625bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes static final int FLAG_SNAP = SCROLL_FLAG_SCROLL | SCROLL_FLAG_SNAP; 6261a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes static final int COLLAPSIBLE_FLAGS = SCROLL_FLAG_EXIT_UNTIL_COLLAPSED 6271a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes | SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED; 628a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 629a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int mScrollFlags = SCROLL_FLAG_SCROLL; 630a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Interpolator mScrollInterpolator; 631a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 632a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(Context c, AttributeSet attrs) { 633a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(c, attrs); 634a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams); 635a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0); 636a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) { 637a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int resId = a.getResourceId( 638a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0); 639a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator( 640a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes c, resId); 641a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 642a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 643a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 644a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 645a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height) { 646a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height); 647a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 648a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 649a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height, float weight) { 650a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height, weight); 651a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 652a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 653a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(ViewGroup.LayoutParams p) { 654a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(p); 655a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 656a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 657a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(MarginLayoutParams source) { 658a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 659a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 660a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 661a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LinearLayout.LayoutParams source) { 662a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 663a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 664a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 665a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LayoutParams source) { 666a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 667a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = source.mScrollFlags; 668a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = source.mScrollInterpolator; 669a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 670a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 671a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 672a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the scrolling flags. 673a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 674a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL}, 675bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS}, 676bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes * {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED} and {@link #SCROLL_FLAG_SNAP }. 677a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 678a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollFlags() 679a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 680149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags 681a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 68250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void setScrollFlags(@ScrollFlags int flags) { 683a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = flags; 684a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 685a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 686a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 687a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the scrolling flags. 688a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 689a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollFlags(int) 690a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 691149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags 692a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 69350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @ScrollFlags 694a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getScrollFlags() { 695a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollFlags; 696a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 697a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 698a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 699a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the interpolator to when scrolling the view associated with this 700a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. 701a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 702a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling. 703a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 704149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator 705a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollInterpolator() 706a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 707a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setScrollInterpolator(Interpolator interpolator) { 708a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = interpolator; 709a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 710a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 711a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 712a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the {@link Interpolator} being used for scrolling the view associated with this 713a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling. 714a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 715149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator 716a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollInterpolator(Interpolator) 717a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 718a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Interpolator getScrollInterpolator() { 719a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollInterpolator; 720a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 7211a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 7221a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes /** 7231a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes * Returns true if the scroll flags are compatible for 'collapsing' 7241a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes */ 7251a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private boolean isCollapsible() { 7261a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes return (mScrollFlags & SCROLL_FLAG_SCROLL) == SCROLL_FLAG_SCROLL 7271a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes && (mScrollFlags & COLLAPSIBLE_FLAGS) != 0; 7281a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 729a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 730a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 731a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 732a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested 733a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll handling with offsetting. 734a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 735ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor public static class Behavior extends HeaderBehavior<AppBarLayout> { 736dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes private static final int ANIMATE_OFFSET_DIPS_PER_SECOND = 300; 737e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private static final int INVALID_POSITION = -1; 738e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 73972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes /** 74072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * Callback to allow control over any {@link AppBarLayout} dragging. 74172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes */ 74272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes public static abstract class DragCallback { 74372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes /** 74472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * Allows control over whether the given {@link AppBarLayout} can be dragged or not. 74572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * 74672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * <p>Dragging is defined as a direct touch on the AppBarLayout with movement. This 74772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * call does not affect any nested scrolling.</p> 74872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * 74972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * @return true if we are in a position to scroll the AppBarLayout via a drag, false 75072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * if not. 75172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes */ 75272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes public abstract boolean canDrag(@NonNull AppBarLayout appBarLayout); 75372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } 75472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes 755e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private int mOffsetDelta; 756a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 75750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private boolean mSkipNestedPreScroll; 75847543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes private boolean mWasNestedFlung; 759631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 760c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov private ValueAnimatorCompat mOffsetAnimator; 76150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 762e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private int mOffsetToChildIndexOnLayout = INVALID_POSITION; 763e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private boolean mOffsetToChildIndexOnLayoutIsMinHeight; 764e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private float mOffsetToChildIndexOnLayoutPerc; 765e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 766d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes private WeakReference<View> mLastNestedScrollingChildRef; 76772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes private DragCallback mOnDragCallback; 768d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 769a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior() {} 770a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 771a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior(Context context, AttributeSet attrs) { 772a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 773a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 774a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 775a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 776ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, 777a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View directTargetChild, View target, int nestedScrollAxes) { 778f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes // Return true if we're nested scrolling vertically, and we have scrollable children 779f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes // and the scrolling view is big enough to scroll 780631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 781f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes && child.hasScrollableChildren() 782ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes && parent.getHeight() - directTargetChild.getHeight() <= child.getHeight(); 783631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 784c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov if (started && mOffsetAnimator != null) { 785631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Cancel any offset animation 786c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.cancel(); 787631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 788631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 789d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes // A new nested scroll has started so clear out the previous ref 790d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mLastNestedScrollingChildRef = null; 791d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 792631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return started; 793a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 794a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 795a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 796a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 797a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dx, int dy, int[] consumed) { 79850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy != 0 && !mSkipNestedPreScroll) { 79950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int min, max; 80050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy < 0) { 80150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling down 80250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getTotalScrollRange(); 80350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = min + child.getDownNestedPreScrollRange(); 80450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 80550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling up 80650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getUpNestedPreScrollRange(); 80750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = 0; 80850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 80950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes consumed[1] = scroll(coordinatorLayout, child, dy, min, max); 810a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 811a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 812a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 813a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 814a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 815a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dxConsumed, int dyConsumed, 816a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int dxUnconsumed, int dyUnconsumed) { 81750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dyUnconsumed < 0) { 81850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If the scrolling view is scrolling down but not consuming, it's probably be at 81950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // the top of it's content 82050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes scroll(coordinatorLayout, child, dyUnconsumed, 82150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes -child.getDownNestedScrollRange(), 0); 82250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Set the expanding flag so that onNestedPreScroll doesn't handle any events 82350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = true; 82450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 82550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // As we're no longer handling nested scrolls, reset the skip flag 82650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 827a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 82850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 829a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 83050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 831bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, 83250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes View target) { 83347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes if (!mWasNestedFlung) { 834bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // If we haven't been flung then let's see if the current view has been set to snap 835bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes snapToChildIfNeeded(coordinatorLayout, abl); 836bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 837bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 838bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // Reset the flags 83950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 84047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes mWasNestedFlung = false; 841d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes // Keep a reference to the previous nested scrolling child 842d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mLastNestedScrollingChildRef = new WeakReference<>(target); 843d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 844d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 845d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes @Override 846631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public boolean onNestedFling(final CoordinatorLayout coordinatorLayout, 847631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final AppBarLayout child, View target, float velocityX, float velocityY, 848631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes boolean consumed) { 849bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes boolean flung = false; 850bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 851631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (!consumed) { 852631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // It has been consumed so let's fling ourselves 853bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes flung = fling(coordinatorLayout, child, -child.getTotalScrollRange(), 854bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 0, -velocityY); 855631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 856631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're scrolling up and the child also consumed the fling. We'll fake scroll 857631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // upto our 'collapsed' offset 858631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (velocityY < 0) { 859631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling down 860bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes final int targetScroll = -child.getTotalScrollRange() 861631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes + child.getDownNestedPreScrollRange(); 862bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes if (getTopBottomOffsetForScrollingSibling() < targetScroll) { 863bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // If we're currently not expanded more than the target scroll, we'll 864bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // animate a fling 865bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes animateOffsetTo(coordinatorLayout, child, targetScroll); 866bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes flung = true; 867631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 868631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 869631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling up 870bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes final int targetScroll = -child.getUpNestedPreScrollRange(); 871bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes if (getTopBottomOffsetForScrollingSibling() > targetScroll) { 872bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // If we're currently not expanded less than the target scroll, we'll 873bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // animate a fling 874bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes animateOffsetTo(coordinatorLayout, child, targetScroll); 875bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes flung = true; 876631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 877631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 878631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 879631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 88047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes mWasNestedFlung = flung; 881bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes return flung; 882631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 883631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 88472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes /** 88572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * Set a callback to control any {@link AppBarLayout} dragging. 88672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * 88772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes * @param callback the callback to use, or {@code null} to use the default behavior. 88872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes */ 88972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes public void setDragCallback(@Nullable DragCallback callback) { 89072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes mOnDragCallback = callback; 89172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } 89272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes 893631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private void animateOffsetTo(final CoordinatorLayout coordinatorLayout, 894dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes final AppBarLayout child, final int offset) { 895dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes final int currentOffset = getTopBottomOffsetForScrollingSibling(); 896dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes if (currentOffset == offset) { 897c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov if (mOffsetAnimator != null && mOffsetAnimator.isRunning()) { 898c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.cancel(); 899dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes } 900dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes return; 901dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes } 902dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes 903c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov if (mOffsetAnimator == null) { 904c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator = ViewUtils.createAnimator(); 905c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR); 906c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() { 907631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 908631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void onAnimationUpdate(ValueAnimatorCompat animator) { 909ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor setHeaderTopBottomOffset(coordinatorLayout, child, 910631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes animator.getAnimatedIntValue()); 911631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 912631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes }); 913631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 914c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.cancel(); 915631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 916631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 917dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes // Set the duration based on the amount of dips we're travelling in 918dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes final float distanceDp = Math.abs(currentOffset - offset) / 919dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes coordinatorLayout.getResources().getDisplayMetrics().density; 920c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.setDuration( 921c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov Math.round(distanceDp * 1000 / ANIMATE_OFFSET_DIPS_PER_SECOND)); 922dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes 923c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.setIntValues(currentOffset, offset); 924c530cc115fddab8e1d9645b322424dd45f9ecd0dKirill Grouchnikov mOffsetAnimator.start(); 925631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 926631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 927bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes private View getChildOnOffset(AppBarLayout abl, final int offset) { 928bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes for (int i = 0, count = abl.getChildCount(); i < count; i++) { 929bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes View child = abl.getChildAt(i); 930bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes if (child.getTop() <= -offset && child.getBottom() >= -offset) { 931bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes return child; 932bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 933bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 934bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes return null; 935bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 936bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 937bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes private void snapToChildIfNeeded(CoordinatorLayout coordinatorLayout, AppBarLayout abl) { 938bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes final int offset = getTopBottomOffsetForScrollingSibling(); 939bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes final View offsetChild = getChildOnOffset(abl, offset); 940bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes if (offsetChild != null) { 941bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes final LayoutParams lp = (LayoutParams) offsetChild.getLayoutParams(); 942bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes if ((lp.getScrollFlags() & LayoutParams.FLAG_SNAP) == LayoutParams.FLAG_SNAP) { 943bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes // We're set the snap, so animate the offset to the nearest edge 944dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes int childTop = -offsetChild.getTop(); 945dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes int childBottom = -offsetChild.getBottom(); 946dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes 947dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes // If the view is set only exit until it is collapsed, we'll abide by that 948dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes if ((lp.getScrollFlags() & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) 949dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes == LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) { 950dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes childBottom += ViewCompat.getMinimumHeight(offsetChild); 951dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes } 952dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes 953dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes final int newOffset = offset < (childBottom + childTop) / 2 954dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes ? childBottom : childTop; 955bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes animateOffsetTo(coordinatorLayout, abl, 956dcde7bf0c79a12f30d3853a82f4fbf54ce5161c8Chris Banes MathUtils.constrain(newOffset, -abl.getTotalScrollRange(), 0)); 957bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 958bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 959bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes } 960bc22c4fd37e99643b3e7b87e2849a9e63fb8c5fcChris Banes 961631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 962bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes public boolean onMeasureChild(CoordinatorLayout parent, AppBarLayout child, 963bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, 964bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes int heightUsed) { 965bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes final CoordinatorLayout.LayoutParams lp = 966bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes (CoordinatorLayout.LayoutParams) child.getLayoutParams(); 967bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes if (lp.height == CoordinatorLayout.LayoutParams.WRAP_CONTENT) { 968bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes // If the view is set to wrap on it's height, CoordinatorLayout by default will 969bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes // cap the view at the CoL's height. Since the AppBarLayout can scroll, this isn't 970bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes // what we actually want, so we measure it ourselves with an unspecified spec to 971bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes // allow the child to be larger than it's parent 972bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes parent.onMeasureChild(child, parentWidthMeasureSpec, widthUsed, 973bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightUsed); 974bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes return true; 975bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes } 976bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes 977bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes // Let the parent handle it as normal 978bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, 979bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes parentHeightMeasureSpec, heightUsed); 980bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes } 981bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes 982bca7a517ce4fb75e2571ce2d9d0263fea8e15dbeChris Banes @Override 9839995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, 98450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int layoutDirection) { 9859995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes boolean handled = super.onLayoutChild(parent, abl, layoutDirection); 9869995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 9879995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final int pendingAction = abl.getPendingAction(); 9889995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (pendingAction != PENDING_ACTION_NONE) { 9899995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final boolean animate = (pendingAction & PENDING_ACTION_ANIMATE_ENABLED) != 0; 9909995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if ((pendingAction & PENDING_ACTION_COLLAPSED) != 0) { 9919995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final int offset = -abl.getUpNestedPreScrollRange(); 9929995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (animate) { 9939995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes animateOffsetTo(parent, abl, offset); 9949995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else { 995ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor setHeaderTopBottomOffset(parent, abl, offset); 9969995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 9979995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else if ((pendingAction & PENDING_ACTION_EXPANDED) != 0) { 9989995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (animate) { 9999995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes animateOffsetTo(parent, abl, 0); 10009995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else { 1001ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor setHeaderTopBottomOffset(parent, abl, 0); 10029995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 10039995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 10049995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else if (mOffsetToChildIndexOnLayout >= 0) { 10059995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes View child = abl.getChildAt(mOffsetToChildIndexOnLayout); 1006e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes int offset = -child.getBottom(); 1007e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (mOffsetToChildIndexOnLayoutIsMinHeight) { 1008e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes offset += ViewCompat.getMinimumHeight(child); 1009e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } else { 1010e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc); 1011e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1012e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes setTopAndBottomOffset(offset); 1013e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 101450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 101579aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes // Finally reset any pending states 101679aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes abl.resetPendingAction(); 101779aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes mOffsetToChildIndexOnLayout = INVALID_POSITION; 101879aa418487cdefb85d3c1fac32d6403a1ac6057dChris Banes 101947543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes // We may have changed size, so let's constrain the top and bottom offset correctly, 102047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes // just in case we're out of the bounds 102147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes setTopAndBottomOffset( 102247543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes MathUtils.constrain(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0)); 102347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes 10241a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // Make sure we dispatch the offset update 10251a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes abl.dispatchOffsetUpdates(getTopAndBottomOffset()); 102650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 102750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return handled; 1028a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1029a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 103072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes @Override 103172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes boolean canDragView(AppBarLayout view) { 103272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes if (mOnDragCallback != null) { 103372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes // If there is a drag callback set, it's in control 103472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes return mOnDragCallback.canDrag(view); 103572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } 103672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes 103772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes // Else we'll use the default behaviour of seeing if it can scroll down 1038d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (mLastNestedScrollingChildRef != null) { 103972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes // If we have a reference to a scrolling view, check it 104072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes final View scrollingView = mLastNestedScrollingChildRef.get(); 104172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes return scrollingView != null && scrollingView.isShown() 104272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes && !ViewCompat.canScrollVertically(scrollingView, -1); 104372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } else { 104472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes // Otherwise we assume that the scrolling view hasn't been scrolled and can drag. 104572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes return true; 1046d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 1047d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 1048d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 1049ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor @Override 105047543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes void onFlingFinished(CoordinatorLayout parent, AppBarLayout layout) { 105147543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes // At the end of a manual fling, check to see if we need to snap to the edge-child 105247543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes snapToChildIfNeeded(parent, layout); 105347543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes } 105447543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes 105547543aa43aacb8defbbf90682fcb2b63ce1b00b5Chris Banes @Override 105672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes int getMaxDragOffset(AppBarLayout view) { 105772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes return -view.getDownNestedScrollRange(); 105872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } 105972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes 106072a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes @Override 106172a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes int getScrollRangeForDragFling(AppBarLayout view) { 106272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes return view.getTotalScrollRange(); 106372a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes } 106472a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes 106572a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes @Override 106672a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes int setHeaderTopBottomOffset(CoordinatorLayout coordinatorLayout, 106772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes AppBarLayout header, int newOffset, int minOffset, int maxOffset) { 1068e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int curOffset = getTopBottomOffsetForScrollingSibling(); 1069a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int consumed = 0; 1070a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 10715e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) { 1072631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we have some scrolling range, and we're currently within the min and max 1073631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // offsets, calculate a new offset 107450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset); 1075ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor AppBarLayout appBarLayout = (AppBarLayout) header; 1076a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (curOffset != newOffset) { 1077e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int interpolatedOffset = appBarLayout.hasChildWithInterpolator() 1078e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ? interpolateOffset(appBarLayout, newOffset) 1079e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes : newOffset; 1080e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 10811a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset); 1082631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 1083a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update how much dy we have consumed 1084a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes consumed = curOffset - newOffset; 1085a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update the stored sibling offset 1086e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetDelta = newOffset - interpolatedOffset; 1087a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1088a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) { 1089a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the offset hasn't changed and we're using an interpolated scroll 1090a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // then we need to keep any dependent views updated. CoL will do this for 1091a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us when we move, but we need to do it manually when we don't (as an 1092a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // interpolated scroll may finish early). 1093a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes coordinatorLayout.dispatchDependentViewsChanged(appBarLayout); 1094a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1095a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1096631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Dispatch the updates to any listeners 10971a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes appBarLayout.dispatchOffsetUpdates(getTopAndBottomOffset()); 10981a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 10991a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // Update the AppBarLayout's drawable state (for any elevation changes) 11001a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes updateAppBarLayoutDrawableState(appBarLayout, newOffset, 11011a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes newOffset < curOffset ? -1 : 1); 1102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 11035e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes } else { 11045e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes // Reset the offset delta 11055e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes mOffsetDelta = 0; 1106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return consumed; 1109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int interpolateOffset(AppBarLayout layout, final int offset) { 1112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int absOffset = Math.abs(offset); 1113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 1115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = layout.getChildAt(i); 1116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 111750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 1118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 111950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (absOffset >= child.getTop() && absOffset <= child.getBottom()) { 1120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 112150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int childScrollableHeight = 0; 112250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int flags = childLp.getScrollFlags(); 112350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 1124b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // We're set to scroll so add the child's height plus margin 1125b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes childScrollableHeight += child.getHeight() + childLp.topMargin 1126b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes + childLp.bottomMargin; 1127b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes 112850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 1129b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // For a collapsing scroll, we to take the collapsed height 1130b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // into account. 113150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight -= ViewCompat.getMinimumHeight(child); 113250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 113350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 113450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 1135ea004a01f44aaf36212120c936952c1742d03d30Chris Banes if (ViewCompat.getFitsSystemWindows(child)) { 1136ea004a01f44aaf36212120c936952c1742d03d30Chris Banes childScrollableHeight -= layout.getTopInset(); 1137ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1138ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 113950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childScrollableHeight > 0) { 114050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int offsetForView = absOffset - child.getTop(); 114150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int interpolatedDiff = Math.round(childScrollableHeight * 114250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes interpolator.getInterpolation( 114350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes offsetForView / (float) childScrollableHeight)); 114450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 114550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return Integer.signum(offset) * (child.getTop() + interpolatedDiff); 114650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 1147a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 114850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 114950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If we get to here then the view on the offset isn't suitable for interpolated 115050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // scrolling. So break out of the loop 115150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes break; 1152a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1153a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1154a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1155a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return offset; 1156a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1157a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 11581a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private void updateAppBarLayoutDrawableState(final AppBarLayout layout, 11591a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final int offset, final int direction) { 11601a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final View child = getAppBarChildOnOffset(layout, offset); 11611a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (child != null) { 11621a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 11631a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final int flags = childLp.getScrollFlags(); 11641a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes boolean collapsed = false; 11651a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 11661a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 11671a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final int minHeight = ViewCompat.getMinimumHeight(child); 11681a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 11691a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (direction > 0 && (flags & (LayoutParams.SCROLL_FLAG_ENTER_ALWAYS 11701a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes | LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED)) != 0) { 11711a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // We're set to enter always collapsed so we are only collapsed when 11721a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // being scrolled down, and in a collapsed offset 11731a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset(); 11741a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 11751a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // We're set to exit until collapsed, so any offset which results in 11761a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes // the minimum height (or less) being shown is collapsed 11771a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset(); 11781a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 11791a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 11801a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 11811a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes layout.setCollapsed(collapsed); 11821a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 11831a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 11841a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 11851a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes private static View getAppBarChildOnOffset(final AppBarLayout layout, final int offset) { 11861a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final int absOffset = Math.abs(offset); 11871a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 11881a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes final View child = layout.getChildAt(i); 11891a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes if (absOffset >= child.getTop() && absOffset <= child.getBottom()) { 11901a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes return child; 11911a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 11921a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 11931a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes return null; 11941a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes } 11951a8e4886abbc35e479cddb7b4acd601cede2fd8aChris Banes 1196ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor @Override 119772a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes int getTopBottomOffsetForScrollingSibling() { 1198e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return getTopAndBottomOffset() + mOffsetDelta; 1199e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1200e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1201e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1202e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout) { 1203e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final Parcelable superState = super.onSaveInstanceState(parent, appBarLayout); 1204e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int offset = getTopAndBottomOffset(); 1205e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1206e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes // Try and find the first visible child... 1207e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes for (int i = 0, count = appBarLayout.getChildCount(); i < count; i++) { 1208e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes View child = appBarLayout.getChildAt(i); 1209e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int visBottom = child.getBottom() + offset; 1210e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1211e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (child.getTop() + offset <= 0 && visBottom >= 0) { 1212e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final SavedState ss = new SavedState(superState); 1213e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibleChildIndex = i; 1214e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibileChildAtMinimumHeight = 1215e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes visBottom == ViewCompat.getMinimumHeight(child); 1216e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibileChildPercentageShown = visBottom / (float) child.getHeight(); 1217e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return ss; 1218e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1219e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1220e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1221e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes // Else we'll just return the super state 1222e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return superState; 1223e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1224e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1225e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1226e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout, 1227e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes Parcelable state) { 1228e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (state instanceof SavedState) { 1229e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final SavedState ss = (SavedState) state; 1230e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.onRestoreInstanceState(parent, appBarLayout, ss.getSuperState()); 1231e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = ss.firstVisibleChildIndex; 1232e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayoutPerc = ss.firstVisibileChildPercentageShown; 1233e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayoutIsMinHeight = ss.firstVisibileChildAtMinimumHeight; 1234e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } else { 1235e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.onRestoreInstanceState(parent, appBarLayout, state); 1236e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = INVALID_POSITION; 1237e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1238e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1239e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 124062ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes protected static class SavedState extends BaseSavedState { 1241e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes int firstVisibleChildIndex; 1242e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes float firstVisibileChildPercentageShown; 1243e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes boolean firstVisibileChildAtMinimumHeight; 1244e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 124562ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes public SavedState(Parcel source, ClassLoader loader) { 1246e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super(source); 1247e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibleChildIndex = source.readInt(); 1248e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibileChildPercentageShown = source.readFloat(); 1249e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibileChildAtMinimumHeight = source.readByte() != 0; 1250e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1251e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1252e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState(Parcelable superState) { 1253e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super(superState); 1254e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1255e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1256e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1257e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public void writeToParcel(Parcel dest, int flags) { 1258e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.writeToParcel(dest, flags); 1259e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeInt(firstVisibleChildIndex); 1260e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeFloat(firstVisibileChildPercentageShown); 1261e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeByte((byte) (firstVisibileChildAtMinimumHeight ? 1 : 0)); 1262e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1263e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1264e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public static final Parcelable.Creator<SavedState> CREATOR = 126562ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() { 1266e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 126762ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes public SavedState createFromParcel(Parcel source, ClassLoader loader) { 126862ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes return new SavedState(source, loader); 1269e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1270e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1271e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1272e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState[] newArray(int size) { 1273e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return new SavedState[size]; 1274e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 127562ab25d71ccfa775a8bd2add7b41277bc4e14bbcChris Banes }); 1276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 1280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Behavior which should be used by {@link View}s which can scroll vertically and support 1281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * nested scrolling to automatically scroll any {@link AppBarLayout} siblings. 1282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 1283ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor public static class ScrollingViewBehavior extends HeaderScrollingViewBehavior { 1284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior() {} 1286a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior(Context context, AttributeSet attrs) { 1288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 1289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1290b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes final TypedArray a = context.obtainStyledAttributes(attrs, 1291a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params); 1292b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes setOverlayTop(a.getDimensionPixelSize( 1293b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0)); 1294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 1295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1296a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { 1299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We depend on any AppBarLayouts 1300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return dependency instanceof AppBarLayout; 1301a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onDependentViewChanged(CoordinatorLayout parent, View child, 1305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View dependency) { 13065e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes offsetChildAsNeeded(parent, child, dependency); 13070d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes return false; 13080d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes } 13090d2f46331a9fc3b21597e926ba2e061af7c00708Chris Banes 13105e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes private void offsetChildAsNeeded(CoordinatorLayout parent, View child, View dependency) { 1311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final CoordinatorLayout.Behavior behavior = 1312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); 1313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (behavior instanceof Behavior) { 13145e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes // Offset the child, pinning it to the bottom the header-dependency, maintaining 13155e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes // any vertical gap, and overlap 13165e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes final Behavior ablBehavior = (Behavior) behavior; 13175e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes final int offset = ablBehavior.getTopBottomOffsetForScrollingSibling(); 1318318baf84dade07174d71e10322e3b10ab4b0c28cChris Banes ViewCompat.offsetTopAndBottom(child, (dependency.getBottom() - child.getTop()) 13195e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes + ablBehavior.mOffsetDelta 13205e7673e0dbd89512b525d1bde5c912eb07885550Chris Banes + getVerticalLayoutGap() 1321b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes - getOverlapPixelsForOffset(dependency)); 1322ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1323ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1324ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 1325b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes 1326b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes @Override 1327b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes float getOverlapRatioForOffset(final View header) { 1328b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes if (header instanceof AppBarLayout) { 1329b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes final AppBarLayout abl = (AppBarLayout) header; 1330ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int totalScrollRange = abl.getTotalScrollRange(); 1331ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int preScrollDown = abl.getDownNestedPreScrollRange(); 1332b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes final int offset = getAppBarLayoutOffset(abl); 1333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1334ea004a01f44aaf36212120c936952c1742d03d30Chris Banes if (preScrollDown != 0 && (totalScrollRange + offset) <= preScrollDown) { 1335ea004a01f44aaf36212120c936952c1742d03d30Chris Banes // If we're in a pre-scroll down. Don't use the offset at all. 1336ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return 0; 1337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 1338ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int availScrollRange = totalScrollRange - preScrollDown; 1339dfb857dd12b359c0bdae11a23e039f286d54dc0aChris Banes if (availScrollRange != 0) { 1340ea004a01f44aaf36212120c936952c1742d03d30Chris Banes // Else we'll use a interpolated ratio of the overlap, depending on offset 1341b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes return 1f + (offset / (float) availScrollRange); 1342ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1345b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes return 0f; 1346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1348b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes private static int getAppBarLayoutOffset(AppBarLayout abl) { 1349b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes final CoordinatorLayout.Behavior behavior = 1350b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes ((CoordinatorLayout.LayoutParams) abl.getLayoutParams()).getBehavior(); 1351b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes if (behavior instanceof Behavior) { 1352b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes return ((Behavior) behavior).getTopBottomOffsetForScrollingSibling(); 1353b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes } 1354b2f568c9c71763ed823b1f9c274077825b5d0c9eChris Banes return 0; 1355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1357ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor @Override 135872a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes View findFirstDependency(List<View> views) { 1359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = views.size(); i < z; i++) { 1360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View view = views.get(i); 1361a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (view instanceof AppBarLayout) { 136272a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes return view; 1363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1365a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return null; 1366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1367ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor 1368ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor @Override 136972a0913607198c5ce3fa351242ccbdfb3b93f178Chris Banes int getScrollRange(View v) { 1370ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor if (v instanceof AppBarLayout) { 1371ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor return ((AppBarLayout) v).getTotalScrollRange(); 1372ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor } else { 1373ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor return super.getScrollRange(v); 1374ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor } 1375ec161ac1da9c8ca0e942b01e037ceb1cc51a2f3cMady Mellor } 1376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes} 1378