AppBarLayout.java revision ea004a01f44aaf36212120c936952c1742d03d30
1a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/* 2a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Copyright (C) 2015 The Android Open Source Project 3a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 4a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * you may not use this file except in compliance with the License. 6a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * You may obtain a copy of the License at 7a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 8a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * http://www.apache.org/licenses/LICENSE-2.0 9a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 10a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Unless required by applicable law or agreed to in writing, software 11a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * See the License for the specific language governing permissions and 14a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * limitations under the License. 15a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 16a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 17a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespackage android.support.design.widget; 18a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 19a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.Context; 20a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.content.res.TypedArray; 21e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcel; 22e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banesimport android.os.Parcelable; 2350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport android.support.annotation.IntDef; 24a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.design.R; 25d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banesimport android.support.v4.view.MotionEventCompat; 26a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat; 276ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banesimport android.support.v4.view.WindowInsetsCompat; 28631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport android.support.v4.widget.ScrollerCompat; 29a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet; 30d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banesimport android.view.MotionEvent; 31a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View; 32d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banesimport android.view.ViewConfiguration; 33a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup; 34a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator; 35a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout; 36a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 3750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.Retention; 3850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.RetentionPolicy; 39d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banesimport java.lang.ref.WeakReference; 40631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.ArrayList; 41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List; 42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/** 44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of 459fb154338a62edc2c57dc036895199d6f1769400Chris Banes * material designs app bar concept, namely scrolling gestures. 46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through 48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute: 49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}. 50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}. 53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will 54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work. 55a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 569fb154338a62edc2c57dc036895199d6f1769400Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to know when to scroll. 579fb154338a62edc2c57dc036895199d6f1769400Chris Banes * The binding is done through the {@link ScrollingViewBehavior} behavior class, meaning that you 589fb154338a62edc2c57dc036895199d6f1769400Chris Banes * should set your scrolling view's behavior to be an instance of {@link ScrollingViewBehavior}. 599fb154338a62edc2c57dc036895199d6f1769400Chris Banes * A string resource containing the full class name is available. 60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre> 62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.CoordinatorLayout 63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:android="http://schemas.android.com/apk/res/android" 64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:app="http://schemas.android.com/apk/res-auto" 65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent"> 67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v4.widget.NestedScrollView 69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent" 71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_behavior="@string/appbar_scrolling_view_behavior"> 72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <!-- Your scrolling content --> 74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.v4.widget.NestedScrollView> 76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.AppBarLayout 78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="wrap_content" 79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent"> 80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v7.widget.Toolbar 82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.TabLayout 86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.AppBarLayout> 90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.CoordinatorLayout> 92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre> 93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar"> 95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a> 96a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 97a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class) 98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout { 99a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1009995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_NONE = 0x0; 1019995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_EXPANDED = 0x1; 1029995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_COLLAPSED = 0x2; 1039995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4; 1049995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 105a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 106631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical 107631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * offset changes. 108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 109631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public interface OnOffsetChangedListener { 110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows 112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * child views to implement custom behavior based on the offset (for instance pinning a 113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view at a certain y value). 114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 115631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param appBarLayout the {@link AppBarLayout} which offset has changed 11650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px 117a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 118631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset); 119a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static final int INVALID_SCROLL_RANGE = -1; 122a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mTotalScrollRange = INVALID_SCROLL_RANGE; 124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownPreScrollRange = INVALID_SCROLL_RANGE; 125a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownScrollRange = INVALID_SCROLL_RANGE; 126a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 127a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes boolean mHaveChildWithInterpolator; 128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 12950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private float mTargetElevation; 13050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 1319995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private int mPendingAction = PENDING_ACTION_NONE; 1329995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 1336ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes private WindowInsetsCompat mLastInsets; 1346ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 1356f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes private final List<OnOffsetChangedListener> mListeners; 136631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 137a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context) { 138a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes this(context, null); 139a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 140a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 141a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context, AttributeSet attrs) { 142a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 143a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setOrientation(VERTICAL); 14450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 145809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes ThemeUtils.checkAppCompatTheme(context); 146809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes 14750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout, 14850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 0, R.style.Widget_Design_AppBarLayout); 14950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0); 15081520564f3dd783136e025174021ba4eabd6ff3cChris Banes setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background)); 1519995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (a.hasValue(R.styleable.AppBarLayout_expanded)) { 1529995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false)); 1539995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 15450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes a.recycle(); 15550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 15650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Use the bounds view outline provider so that we cast a shadow, even without a background 15750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewUtils.setBoundsViewOutlineProvider(this); 158631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 159631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mListeners = new ArrayList<>(); 160631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 161631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.setElevation(this, mTargetElevation); 1626ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 1636ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes ViewCompat.setOnApplyWindowInsetsListener(this, 1646ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes new android.support.v4.view.OnApplyWindowInsetsListener() { 1656ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes @Override 1666ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes public WindowInsetsCompat onApplyWindowInsets(View v, 1676ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes WindowInsetsCompat insets) { 1686ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes setWindowInsets(insets); 1696ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes return insets.consumeSystemWindowInsets(); 1706ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 1716ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes }); 172631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 173631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 174631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 175631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Add a listener that will be called when the offset of this {@link AppBarLayout} changes. 176631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 177631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener The listener that will be called when the offset changes.] 178631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 179631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @see #removeOnOffsetChangedListener(OnOffsetChangedListener) 180631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 181631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void addOnOffsetChangedListener(OnOffsetChangedListener listener) { 1826f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes if (listener != null && !mListeners.contains(listener)) { 1836f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes mListeners.add(listener); 184631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 185631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 186631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 187631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 188631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Remove the previously added {@link OnOffsetChangedListener}. 189631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 190631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener the listener to remove. 191631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 192631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) { 1936f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes if (listener != null) { 1946f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes mListeners.remove(listener); 195631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 198a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 199ea004a01f44aaf36212120c936952c1742d03d30Chris Banes protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 200ea004a01f44aaf36212120c936952c1742d03d30Chris Banes super.onMeasure(widthMeasureSpec, heightMeasureSpec); 201ea004a01f44aaf36212120c936952c1742d03d30Chris Banes invalidateScrollRanges(); 202ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 203ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 204ea004a01f44aaf36212120c936952c1742d03d30Chris Banes @Override 205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected void onLayout(boolean changed, int l, int t, int r, int b) { 206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.onLayout(changed, l, t, r, b); 207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = false; 209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = true; 216a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 217a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 221ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private void invalidateScrollRanges() { 222ea004a01f44aaf36212120c936952c1742d03d30Chris Banes // Invalidate the scroll ranges 223ea004a01f44aaf36212120c936952c1742d03d30Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 224ea004a01f44aaf36212120c936952c1742d03d30Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 225ea004a01f44aaf36212120c936952c1742d03d30Chris Banes mDownScrollRange = INVALID_SCROLL_RANGE; 226ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 227ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOrientation(int orientation) { 230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (orientation != VERTICAL) { 231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes throw new IllegalArgumentException("AppBarLayout is always vertical and does" 232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes + " not support horizontal orientation"); 233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 234a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.setOrientation(orientation); 235a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 2379995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes /** 2389995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * Sets whether this {@link AppBarLayout} is expanded or not, animating if it has already 2399995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * been laid out. 2409995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2419995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a 2429995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * direct child of a {@link CoordinatorLayout}.</p> 2439995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2449995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param expanded true if the layout should be fully expanded, false if it should 2459995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * be fully collapsed 2469995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2479995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_expanded 2489995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes */ 2499995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public void setExpanded(boolean expanded) { 2509995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes setExpanded(expanded, ViewCompat.isLaidOut(this)); 2519995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 2529995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 2539995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes /** 2549995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * Sets whether this {@link AppBarLayout} is expanded or not. 2559995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2569995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a 2579995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * direct child of a {@link CoordinatorLayout}.</p> 2589995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2599995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param expanded true if the layout should be fully expanded, false if it should 2609995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * be fully collapsed 2619995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @param animate Whether to animate to the new state 2629995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * 2639995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_expanded 2649995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes */ 2659995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public void setExpanded(boolean expanded, boolean animate) { 2669995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mPendingAction = (expanded ? PENDING_ACTION_EXPANDED : PENDING_ACTION_COLLAPSED) 2679995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes | (animate ? PENDING_ACTION_ANIMATE_ENABLED : 0); 2689995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes requestLayout(); 2699995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 2709995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return p instanceof LayoutParams; 274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateDefaultLayoutParams() { 278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams generateLayoutParams(AttributeSet attrs) { 283a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(getContext(), attrs); 284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 286a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (p instanceof LinearLayout.LayoutParams) { 289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((LinearLayout.LayoutParams) p); 290a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (p instanceof MarginLayoutParams) { 291a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((MarginLayoutParams) p); 292a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 293a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(p); 294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 296ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private boolean hasChildWithInterpolator() { 297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mHaveChildWithInterpolator; 298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 3016ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes * Returns the scroll range of all children. 302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @return the scroll range in px 304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 3056ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes public final int getTotalScrollRange() { 306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mTotalScrollRange != INVALID_SCROLL_RANGE) { 307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange; 308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 314ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int childHeight = child.getMeasuredHeight(); 315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 319b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes range += childHeight + lp.topMargin + lp.bottomMargin; 320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 3236ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // We also break straight away since later views can't scroll beneath 324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us 3256ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes range -= ViewCompat.getMinimumHeight(child); 3266ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes break; 327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 334ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return mTotalScrollRange = (range - getTopInset()); 335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 337ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private boolean hasScrollableChildren() { 338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return getTotalScrollRange() != 0; 339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling up from a nested pre-scroll. 343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 344ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private int getUpNestedPreScrollRange() { 34550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return getTotalScrollRange(); 346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested pre-scroll. 350a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 351ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private int getDownNestedPreScrollRange() { 352a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownPreScrollRange != INVALID_SCROLL_RANGE) { 353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange; 355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 361ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int childHeight = child.getMeasuredHeight(); 362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) { 365b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // First take the margin into account 366b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes range += lp.topMargin + lp.bottomMargin; 367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // The view has the quick return flag combination... 368a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) { 369a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If they're set to enter collapsed, use the minimum height 370a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += ViewCompat.getMinimumHeight(child); 371a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 372a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Else use the full height 373a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (range > 0) { 376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we've hit an non-quick return scrollable view, and we've already hit a 377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // quick return view, return now 378a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 379a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 380a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange = range; 382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 384a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 385a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested scroll. 386a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 387ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private int getDownNestedScrollRange() { 388a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownScrollRange != INVALID_SCROLL_RANGE) { 389a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 390a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange; 391a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 392a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 393a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 394c1ce4f6cf4d99d6856c4259bf1bb1cb56f604737Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 397ea004a01f44aaf36212120c936952c1742d03d30Chris Banes int childHeight = child.getMeasuredHeight(); 398b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes childHeight += lp.topMargin + lp.bottomMargin; 399a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 400a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 404a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 405a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 406a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 407a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing exit scroll, we to take the collapsed height into account. 408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We also return the range straight away since later views can't scroll 409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // beneath us 410ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return mDownScrollRange = (range - ViewCompat.getMinimumHeight(child)); 411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 417a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange = range; 419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 420a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 42150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int getMinimumHeightForVisibleOverlappingContent() { 4226ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes final int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; 42350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int minHeight = ViewCompat.getMinimumHeight(this); 42450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (minHeight != 0) { 42550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If this layout has a min height, use it (doubled) 4266ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes return (minHeight * 2) + topInset; 42750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 42850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 42950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Otherwise, we'll use twice the min height of our last child 43050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childCount = getChildCount(); 43150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return childCount >= 1 4326ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes ? (ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2) + topInset 43350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes : 0; 43450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 43550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 43650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** 437631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Set the elevation value to use when this {@link AppBarLayout} should be elevated 438631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * above content. 439631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * <p> 440631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * This method does not do anything itself. A typical use for this method is called from within 441631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * an {@link OnOffsetChangedListener} when the offset has changed in such a way to require an 442631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * elevation change. 443631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 444631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param elevation the elevation value to use. 445631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 446631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @see ViewCompat#setElevation(View, float) 44750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes */ 448631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void setTargetElevation(float elevation) { 449631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mTargetElevation = elevation; 450631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 451631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 452631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 453631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Returns the elevation value to use when this {@link AppBarLayout} should be elevated 454631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * above content. 455631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 456631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public float getTargetElevation() { 45750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return mTargetElevation; 45850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 45950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 460ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private int getPendingAction() { 4619995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes return mPendingAction; 4629995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 4639995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 464ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private void resetPendingAction() { 4659995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mPendingAction = PENDING_ACTION_NONE; 4669995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 4679995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 468ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private int getTopInset() { 469ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; 470ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 471ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 4726ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes private void setWindowInsets(WindowInsetsCompat insets) { 4736ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // Invalidate the total scroll range... 4746ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 4756ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes mLastInsets = insets; 4766ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 4776ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // Now dispatch them to our children 4786ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 4796ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes final View child = getChildAt(i); 4806ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes insets = ViewCompat.dispatchApplyWindowInsets(child, insets); 4816ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes if (insets.isConsumed()) { 4826ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes break; 4836ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 4846ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 4856ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes } 4866ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes 487a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class LayoutParams extends LinearLayout.LayoutParams { 48850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 48950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** @hide */ 49050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @IntDef(flag=true, value={ 49150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_SCROLL, 49250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_EXIT_UNTIL_COLLAPSED, 49350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS, 49450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED 49550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes }) 49650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Retention(RetentionPolicy.SOURCE) 49750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public @interface ScrollFlags {} 49850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 499a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 500a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The view will be scroll in direct relation to scroll events. This flag needs to be 501a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * set for any of the other flags to take effect. If any sibling views 502a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * before this one do not have this flag, then this value has no effect. 503a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 504a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_SCROLL = 0x1; 505a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 506a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 507a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When exiting (scrolling off screen) the view will be scrolled until it is 508a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 'collapsed'. The collapsed height is defined by the view's minimum height. 509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 511a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 512a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 513a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2; 514a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 515a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 516a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When entering (scrolling on screen) the view will scroll on any downwards 517a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll event, regardless of whether the scrolling view is also scrolling. This 518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * is commonly referred to as the 'quick return' pattern. 519a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 520a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4; 521a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 522a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 523a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * An additional flag for 'enterAlways' which modifies the returning view to 524a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * only initially scroll back to it's collapsed height. Once the scrolling view has 525a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * reached the end of it's scroll range, the remainder of this view will be scrolled 526a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * into view. The collapsed height is defined by the view's minimum height. 527a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 528a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 529a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 530a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 531a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8; 532a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 533a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 534a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Internal flag which allows quick checking of 'quick return' 535a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 536a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS; 537a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 538a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int mScrollFlags = SCROLL_FLAG_SCROLL; 539a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Interpolator mScrollInterpolator; 540a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 541a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(Context c, AttributeSet attrs) { 542a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(c, attrs); 543a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams); 544a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0); 545a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) { 546a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int resId = a.getResourceId( 547a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0); 548a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator( 549a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes c, resId); 550a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 551a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 552a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 553a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 554a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height) { 555a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height); 556a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 557a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 558a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height, float weight) { 559a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height, weight); 560a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 561a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 562a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(ViewGroup.LayoutParams p) { 563a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(p); 564a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 565a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(MarginLayoutParams source) { 567a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 569a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 570a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LinearLayout.LayoutParams source) { 571a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 572a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 573a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 574a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LayoutParams source) { 575a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 576a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = source.mScrollFlags; 577a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = source.mScrollInterpolator; 578a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 579a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 580a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 581a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the scrolling flags. 582a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 583a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL}, 584a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS} 585a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * and {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED}. 586a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 587a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollFlags() 588a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 589149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags 590a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 59150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void setScrollFlags(@ScrollFlags int flags) { 592a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = flags; 593a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 594a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 596a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the scrolling flags. 597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollFlags(int) 599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 600149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollFlags 601a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 60250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @ScrollFlags 603a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getScrollFlags() { 604a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollFlags; 605a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 606a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 607a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 608a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the interpolator to when scrolling the view associated with this 609a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. 610a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 611a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling. 612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 613149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator 614a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollInterpolator() 615a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 616a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setScrollInterpolator(Interpolator interpolator) { 617a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = interpolator; 618a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 619a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 620a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 621a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the {@link Interpolator} being used for scrolling the view associated with this 622a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling. 623a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 624149689d3a0b4165577470d7152112674d1d7f87cChris Banes * @attr ref android.support.design.R.styleable#AppBarLayout_LayoutParams_layout_scrollInterpolator 625a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollInterpolator(Interpolator) 626a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 627a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Interpolator getScrollInterpolator() { 628a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollInterpolator; 629a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 630a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 631a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 632a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 633a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested 634a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll handling with offsetting. 635a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 636a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class Behavior extends ViewOffsetBehavior<AppBarLayout> { 637d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes private static final int INVALID_POINTER = -1; 638e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private static final int INVALID_POSITION = -1; 639e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 640e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private int mOffsetDelta; 641a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 64250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private boolean mSkipNestedPreScroll; 643631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private Runnable mFlingRunnable; 644631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private ScrollerCompat mScroller; 645631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 646631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private ValueAnimatorCompat mAnimator; 64750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 648e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private int mOffsetToChildIndexOnLayout = INVALID_POSITION; 649e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private boolean mOffsetToChildIndexOnLayoutIsMinHeight; 650e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes private float mOffsetToChildIndexOnLayoutPerc; 651e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 652d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes private boolean mIsBeingDragged; 653d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes private int mActivePointerId = INVALID_POINTER; 654d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes private int mLastMotionY; 655d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes private int mTouchSlop = -1; 656d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 657d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes private WeakReference<View> mLastNestedScrollingChildRef; 658d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 659a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior() {} 660a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 661a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior(Context context, AttributeSet attrs) { 662a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 663a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 664a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 665a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 666ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, 667a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View directTargetChild, View target, int nestedScrollAxes) { 668f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes // Return true if we're nested scrolling vertically, and we have scrollable children 669f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes // and the scrolling view is big enough to scroll 670631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 671f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes && child.hasScrollableChildren() 672ab809dd75ef2f73b312038f2c10473cfa5885a58Chris Banes && parent.getHeight() - directTargetChild.getHeight() <= child.getHeight(); 673631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 674631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (started && mAnimator != null) { 675631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Cancel any offset animation 676631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.cancel(); 677631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 678631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 679d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes // A new nested scroll has started so clear out the previous ref 680d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mLastNestedScrollingChildRef = null; 681d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 682631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return started; 683a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 684a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 685a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 686a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 687a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dx, int dy, int[] consumed) { 68850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy != 0 && !mSkipNestedPreScroll) { 68950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int min, max; 69050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy < 0) { 69150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling down 69250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getTotalScrollRange(); 69350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = min + child.getDownNestedPreScrollRange(); 69450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 69550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling up 69650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getUpNestedPreScrollRange(); 69750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = 0; 69850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 69950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes consumed[1] = scroll(coordinatorLayout, child, dy, min, max); 700a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 701a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 702a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 703a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 704a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 705a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dxConsumed, int dyConsumed, 706a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int dxUnconsumed, int dyUnconsumed) { 70750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dyUnconsumed < 0) { 70850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If the scrolling view is scrolling down but not consuming, it's probably be at 70950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // the top of it's content 71050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes scroll(coordinatorLayout, child, dyUnconsumed, 71150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes -child.getDownNestedScrollRange(), 0); 71250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Set the expanding flag so that onNestedPreScroll doesn't handle any events 71350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = true; 71450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 71550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // As we're no longer handling nested scrolls, reset the skip flag 71650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 717a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 71850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 719a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 72050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 72150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 72250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes View target) { 72350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Reset the skip flag 72450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 725d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes // Keep a reference to the previous nested scrolling child 726d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mLastNestedScrollingChildRef = new WeakReference<>(target); 727d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 728d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 729d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes @Override 730d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, 731d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes MotionEvent ev) { 732d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (mTouchSlop < 0) { 733d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mTouchSlop = ViewConfiguration.get(parent.getContext()).getScaledTouchSlop(); 734d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 735d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 736d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes final int action = ev.getAction(); 737d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 738d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes // Shortcut since we're being dragged 739d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (action == MotionEvent.ACTION_MOVE && mIsBeingDragged) { 740d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes return true; 741d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 742d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 743d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes switch (MotionEventCompat.getActionMasked(ev)) { 744d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes case MotionEvent.ACTION_MOVE: { 745d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes final int activePointerId = mActivePointerId; 746d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (activePointerId == INVALID_POINTER) { 747d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes // If we don't have a valid id, the touch down wasn't on content. 748d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes break; 749d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 750d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId); 751d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (pointerIndex == -1) { 752d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes break; 753d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 754d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 755d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes final int y = (int) MotionEventCompat.getY(ev, pointerIndex); 756d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes final int yDiff = Math.abs(y - mLastMotionY); 757d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (yDiff > mTouchSlop) { 758d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mIsBeingDragged = true; 759d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mLastMotionY = y; 760d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 761d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes break; 762d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 763d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 764d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes case MotionEvent.ACTION_DOWN: { 765d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mIsBeingDragged = false; 766d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes final int x = (int) ev.getX(); 767d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes final int y = (int) ev.getY(); 768d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (parent.isPointInChildBounds(child, x, y) && canDragAppBarLayout()) { 769d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mLastMotionY = y; 770d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 771d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 772d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes break; 773d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 774d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 775d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes case MotionEvent.ACTION_CANCEL: 776d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes case MotionEvent.ACTION_UP: 777d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mIsBeingDragged = false; 778d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mActivePointerId = INVALID_POINTER; 779d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes break; 780d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 781d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 782d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes return mIsBeingDragged; 783d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 784d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 785d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes @Override 786d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes public boolean onTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) { 787d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (mTouchSlop < 0) { 788d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mTouchSlop = ViewConfiguration.get(parent.getContext()).getScaledTouchSlop(); 789d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 790d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 791d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes int x = (int) ev.getX(); 792d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes int y = (int) ev.getY(); 793d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 794d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes switch (MotionEventCompat.getActionMasked(ev)) { 795d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes case MotionEvent.ACTION_DOWN: 796d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (parent.isPointInChildBounds(child, x, y) && canDragAppBarLayout()) { 797d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mLastMotionY = y; 798d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 799d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } else { 800d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes return false; 801d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 802d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes break; 803d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes case MotionEvent.ACTION_MOVE: 804d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, 805d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mActivePointerId); 806d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (activePointerIndex == -1) { 807d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes return false; 808d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 809d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 810d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes y = (int) MotionEventCompat.getY(ev, activePointerIndex); 811d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 812d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes int dy = mLastMotionY - y; 813d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (!mIsBeingDragged && Math.abs(dy) > mTouchSlop) { 814d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mIsBeingDragged = true; 815d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (dy > 0) { 816d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes dy -= mTouchSlop; 817d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } else { 818d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes dy += mTouchSlop; 819d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 820d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 821d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 822d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (mIsBeingDragged) { 823d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mLastMotionY = y; 824d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes // We're being dragged so scroll the ABL 825d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes scroll(parent, child, dy, -child.getDownNestedScrollRange(), 0); 826d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 827d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes break; 828d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes case MotionEvent.ACTION_UP: 829d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes case MotionEvent.ACTION_CANCEL: 830d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mIsBeingDragged = false; 831d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes mActivePointerId = INVALID_POINTER; 832d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes break; 833d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 834d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 835d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes return true; 83650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 83750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 83850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 839631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public boolean onNestedFling(final CoordinatorLayout coordinatorLayout, 840631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final AppBarLayout child, View target, float velocityX, float velocityY, 841631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes boolean consumed) { 842631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (!consumed) { 843631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // It has been consumed so let's fling ourselves 844631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return fling(coordinatorLayout, child, -child.getTotalScrollRange(), 0, -velocityY); 845631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 846631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're scrolling up and the child also consumed the fling. We'll fake scroll 847631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // upto our 'collapsed' offset 848631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes int targetScroll; 849631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (velocityY < 0) { 850631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling down 851631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes targetScroll = -child.getTotalScrollRange() 852631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes + child.getDownNestedPreScrollRange(); 853631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 854631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (getTopBottomOffsetForScrollingSibling() > targetScroll) { 855631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're currently expanded more than the target scroll, we'll return false 856631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // now. This is so that we don't 'scroll' the wrong way. 857631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 858631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 859631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 860631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling up 861631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes targetScroll = -child.getUpNestedPreScrollRange(); 862631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 863631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (getTopBottomOffsetForScrollingSibling() < targetScroll) { 864631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're currently expanded less than the target scroll, we'll return 865631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // false now. This is so that we don't 'scroll' the wrong way. 866631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 867631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 868631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 869631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 870e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (getTopBottomOffsetForScrollingSibling() != targetScroll) { 871631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes animateOffsetTo(coordinatorLayout, child, targetScroll); 872631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return true; 873631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 874631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 875631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 876631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 877631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 878631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 879631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private void animateOffsetTo(final CoordinatorLayout coordinatorLayout, 880631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final AppBarLayout child, int offset) { 881631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mAnimator == null) { 882631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator = ViewUtils.createAnimator(); 883631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR); 884631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() { 885631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 886631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void onAnimationUpdate(ValueAnimatorCompat animator) { 887631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes setAppBarTopBottomOffset(coordinatorLayout, child, 888631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes animator.getAnimatedIntValue()); 889631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 890631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes }); 891631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 892631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.cancel(); 893631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 894631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 895631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setIntValues(getTopBottomOffsetForScrollingSibling(), offset); 896631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.start(); 897631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 898631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 899631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private boolean fling(CoordinatorLayout coordinatorLayout, AppBarLayout layout, int minOffset, 900631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes int maxOffset, float velocityY) { 901631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mFlingRunnable != null) { 902631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes layout.removeCallbacks(mFlingRunnable); 903631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 904631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 905631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mScroller == null) { 906631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mScroller = ScrollerCompat.create(layout.getContext()); 907631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 908631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 909631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mScroller.fling( 910e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 0, getTopBottomOffsetForScrollingSibling(), // curr 911631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 0, Math.round(velocityY), // velocity. 912631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 0, 0, // x 913631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes minOffset, maxOffset); // y 914631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 915631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mScroller.computeScrollOffset()) { 916631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mFlingRunnable = new FlingRunnable(coordinatorLayout, layout); 917631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.postOnAnimation(layout, mFlingRunnable); 918631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return true; 919631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 920631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mFlingRunnable = null; 921631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 922631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 923631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 924631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 925631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private class FlingRunnable implements Runnable { 926631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private final CoordinatorLayout mParent; 927631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private final AppBarLayout mLayout; 928631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 929631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes FlingRunnable(CoordinatorLayout parent, AppBarLayout layout) { 930631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mParent = parent; 931631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mLayout = layout; 932631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 933631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 934631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 935631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void run() { 936631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mLayout != null && mScroller != null && mScroller.computeScrollOffset()) { 937631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes setAppBarTopBottomOffset(mParent, mLayout, mScroller.getCurrY()); 938631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 939631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Post ourselves so that we run on the next animation 940631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.postOnAnimation(mLayout, this); 941631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 942631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 943631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 944631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 945631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 9469995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, 94750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int layoutDirection) { 9489995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes boolean handled = super.onLayoutChild(parent, abl, layoutDirection); 9499995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 9509995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final int pendingAction = abl.getPendingAction(); 9519995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (pendingAction != PENDING_ACTION_NONE) { 9529995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final boolean animate = (pendingAction & PENDING_ACTION_ANIMATE_ENABLED) != 0; 9539995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if ((pendingAction & PENDING_ACTION_COLLAPSED) != 0) { 9549995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes final int offset = -abl.getUpNestedPreScrollRange(); 9559995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (animate) { 9569995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes animateOffsetTo(parent, abl, offset); 9579995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else { 9589995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes setAppBarTopBottomOffset(parent, abl, offset); 9599995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 9609995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else if ((pendingAction & PENDING_ACTION_EXPANDED) != 0) { 9619995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (animate) { 9629995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes animateOffsetTo(parent, abl, 0); 9639995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else { 9649995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes setAppBarTopBottomOffset(parent, abl, 0); 9659995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 9669995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 9679995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes // Finally reset the pending state 9689995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes abl.resetPendingAction(); 9699995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } else if (mOffsetToChildIndexOnLayout >= 0) { 9709995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes View child = abl.getChildAt(mOffsetToChildIndexOnLayout); 971e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes int offset = -child.getBottom(); 972e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (mOffsetToChildIndexOnLayoutIsMinHeight) { 973e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes offset += ViewCompat.getMinimumHeight(child); 974e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } else { 975e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc); 976e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 977e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes setTopAndBottomOffset(offset); 978e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = INVALID_POSITION; 979e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 98050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 98150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Make sure we update the elevation 9829995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes dispatchOffsetUpdates(abl); 98350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 98450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return handled; 985a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 986a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 987a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout, 98850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int dy, int minOffset, int maxOffset) { 989a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, 990e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes getTopBottomOffsetForScrollingSibling() - dy, minOffset, maxOffset); 991a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 992a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 993d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes private boolean canDragAppBarLayout() { 994d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes if (mLastNestedScrollingChildRef != null) { 995d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes final View view = mLastNestedScrollingChildRef.get(); 996d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes return view != null && view.isShown() && !ViewCompat.canScrollVertically(view, -1); 997d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 998d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes return false; 999d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes } 1000d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes 1001631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout, 1002631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes AppBarLayout appBarLayout, int newOffset) { 1003631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, newOffset, 1004631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes Integer.MIN_VALUE, Integer.MAX_VALUE); 1005631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 1006631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 1007631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout, 100850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) { 1009e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int curOffset = getTopBottomOffsetForScrollingSibling(); 1010a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int consumed = 0; 1011a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1012631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) { 1013631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we have some scrolling range, and we're currently within the min and max 1014631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // offsets, calculate a new offset 101550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset); 1016a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1017a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (curOffset != newOffset) { 1018e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int interpolatedOffset = appBarLayout.hasChildWithInterpolator() 1019e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ? interpolateOffset(appBarLayout, newOffset) 1020e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes : newOffset; 1021e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1022e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset); 1023631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 1024a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update how much dy we have consumed 1025a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes consumed = curOffset - newOffset; 1026a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update the stored sibling offset 1027e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetDelta = newOffset - interpolatedOffset; 1028a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1029a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) { 1030a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the offset hasn't changed and we're using an interpolated scroll 1031a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // then we need to keep any dependent views updated. CoL will do this for 1032a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us when we move, but we need to do it manually when we don't (as an 1033a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // interpolated scroll may finish early). 1034a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes coordinatorLayout.dispatchDependentViewsChanged(appBarLayout); 1035a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1036a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1037631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Dispatch the updates to any listeners 1038631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes dispatchOffsetUpdates(appBarLayout); 1039a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1040a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1041a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1042a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return consumed; 1043a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1044a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1045631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private void dispatchOffsetUpdates(AppBarLayout layout) { 10466f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes final List<OnOffsetChangedListener> listeners = layout.mListeners; 104750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 1048631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Iterate backwards through the list so that most recently added listeners 1049631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // get the first chance to decide 1050631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes for (int i = 0, z = listeners.size(); i < z; i++) { 10516f730c0acfb10a929172ea2981a1aded0e39f5c7Chris Banes final OnOffsetChangedListener listener = listeners.get(i); 1052631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (listener != null) { 1053631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes listener.onOffsetChanged(layout, getTopAndBottomOffset()); 1054a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1055a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1056a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1057a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1058a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int interpolateOffset(AppBarLayout layout, final int offset) { 1059a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int absOffset = Math.abs(offset); 1060a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1061a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 1062a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = layout.getChildAt(i); 1063a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 106450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 1065a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 106650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (absOffset >= child.getTop() && absOffset <= child.getBottom()) { 1067a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 106850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int childScrollableHeight = 0; 106950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int flags = childLp.getScrollFlags(); 107050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 1071b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // We're set to scroll so add the child's height plus margin 1072b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes childScrollableHeight += child.getHeight() + childLp.topMargin 1073b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes + childLp.bottomMargin; 1074b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes 107550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 1076b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // For a collapsing scroll, we to take the collapsed height 1077b263b91ba75dc1a7855186e8b0e9cf75d46a6b8dChris Banes // into account. 107850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight -= ViewCompat.getMinimumHeight(child); 107950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 108050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 108150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 1082ea004a01f44aaf36212120c936952c1742d03d30Chris Banes if (ViewCompat.getFitsSystemWindows(child)) { 1083ea004a01f44aaf36212120c936952c1742d03d30Chris Banes childScrollableHeight -= layout.getTopInset(); 1084ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1085ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 108650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childScrollableHeight > 0) { 108750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int offsetForView = absOffset - child.getTop(); 108850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int interpolatedDiff = Math.round(childScrollableHeight * 108950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes interpolator.getInterpolation( 109050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes offsetForView / (float) childScrollableHeight)); 109150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 109250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return Integer.signum(offset) * (child.getTop() + interpolatedDiff); 109350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 1094a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 109550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 109650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If we get to here then the view on the offset isn't suitable for interpolated 109750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // scrolling. So break out of the loop 109850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes break; 1099a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1100a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return offset; 1103a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1105a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getTopBottomOffsetForScrollingSibling() { 1106e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return getTopAndBottomOffset() + mOffsetDelta; 1107e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1108e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1109e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1110e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout) { 1111e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final Parcelable superState = super.onSaveInstanceState(parent, appBarLayout); 1112e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int offset = getTopAndBottomOffset(); 1113e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1114e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes // Try and find the first visible child... 1115e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes for (int i = 0, count = appBarLayout.getChildCount(); i < count; i++) { 1116e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes View child = appBarLayout.getChildAt(i); 1117e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final int visBottom = child.getBottom() + offset; 1118e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1119e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (child.getTop() + offset <= 0 && visBottom >= 0) { 1120e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final SavedState ss = new SavedState(superState); 1121e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibleChildIndex = i; 1122e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibileChildAtMinimumHeight = 1123e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes visBottom == ViewCompat.getMinimumHeight(child); 1124e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes ss.firstVisibileChildPercentageShown = visBottom / (float) child.getHeight(); 1125e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return ss; 1126e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1127e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1128e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1129e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes // Else we'll just return the super state 1130e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return superState; 1131e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1132e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1133e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1134e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout, 1135e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes Parcelable state) { 1136e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes if (state instanceof SavedState) { 1137e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes final SavedState ss = (SavedState) state; 1138e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.onRestoreInstanceState(parent, appBarLayout, ss.getSuperState()); 1139e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = ss.firstVisibleChildIndex; 1140e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayoutPerc = ss.firstVisibileChildPercentageShown; 1141e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayoutIsMinHeight = ss.firstVisibileChildAtMinimumHeight; 1142e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } else { 1143e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.onRestoreInstanceState(parent, appBarLayout, state); 1144e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes mOffsetToChildIndexOnLayout = INVALID_POSITION; 1145e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1146e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1147e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1148e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes protected static class SavedState extends View.BaseSavedState { 1149e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes int firstVisibleChildIndex; 1150e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes float firstVisibileChildPercentageShown; 1151e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes boolean firstVisibileChildAtMinimumHeight; 1152e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1153e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState(Parcel source) { 1154e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super(source); 1155e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibleChildIndex = source.readInt(); 1156e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibileChildPercentageShown = source.readFloat(); 1157e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes firstVisibileChildAtMinimumHeight = source.readByte() != 0; 1158e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1159e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1160e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState(Parcelable superState) { 1161e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super(superState); 1162e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1163e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1164e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1165e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public void writeToParcel(Parcel dest, int flags) { 1166e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes super.writeToParcel(dest, flags); 1167e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeInt(firstVisibleChildIndex); 1168e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeFloat(firstVisibileChildPercentageShown); 1169e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes dest.writeByte((byte) (firstVisibileChildAtMinimumHeight ? 1 : 0)); 1170e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1171e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1172e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public static final Parcelable.Creator<SavedState> CREATOR = 1173e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes new Parcelable.Creator<SavedState>() { 1174e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1175e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState createFromParcel(Parcel source) { 1176e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return new SavedState(source); 1177e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1178e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes 1179e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes @Override 1180e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes public SavedState[] newArray(int size) { 1181e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes return new SavedState[size]; 1182e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes } 1183e52e16988e2500e20052ddd70920f950f4eb4f79Chris Banes }; 1184a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1185a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1186a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1187a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 1188a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Behavior which should be used by {@link View}s which can scroll vertically and support 1189a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * nested scrolling to automatically scroll any {@link AppBarLayout} siblings. 1190a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 1191a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class ScrollingViewBehavior extends ViewOffsetBehavior<View> { 1192a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mOverlayTop; 1193a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1194a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior() {} 1195a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior(Context context, AttributeSet attrs) { 1197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 1198a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1199a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, 1200a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params); 1201a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = a.getDimensionPixelSize( 1202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0); 1203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 1204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { 1208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We depend on any AppBarLayouts 1209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return dependency instanceof AppBarLayout; 1210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1213ea004a01f44aaf36212120c936952c1742d03d30Chris Banes public boolean onMeasureChild(CoordinatorLayout parent, final View child, 1214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentWidthMeasureSpec, int widthUsed, 1215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentHeightMeasureSpec, int heightUsed) { 1216d6c56d929f7975eb6d80116e915ff5515a294c41Chris Banes final int childLpHeight = child.getLayoutParams().height; 1217d6c56d929f7975eb6d80116e915ff5515a294c41Chris Banes if (childLpHeight == LayoutParams.MATCH_PARENT 1218d6c56d929f7975eb6d80116e915ff5515a294c41Chris Banes || childLpHeight == LayoutParams.WRAP_CONTENT) { 1219d6c56d929f7975eb6d80116e915ff5515a294c41Chris Banes // If the child's height is set to match_parent/wrap_content then measure it 1220d6c56d929f7975eb6d80116e915ff5515a294c41Chris Banes // with the maximum visible height 1221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final List<View> dependencies = parent.getDependencies(child); 1223a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dependencies.isEmpty()) { 1224a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we don't have any dependencies, return false 1225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 1226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout appBar = findFirstAppBarLayout(dependencies); 1229ea004a01f44aaf36212120c936952c1742d03d30Chris Banes if (appBar != null && appBar.getVisibility() == View.VISIBLE) { 1230ea004a01f44aaf36212120c936952c1742d03d30Chris Banes if (appBar.getMeasuredHeight() <= 0 && appBar.getMeasuredWidth() <= 0) { 1231ea004a01f44aaf36212120c936952c1742d03d30Chris Banes // The AppBar hasn't been measured yet, so we'll post a Runnable to delay 1232ea004a01f44aaf36212120c936952c1742d03d30Chris Banes // our measure until after the ABLs has happened 1233ea004a01f44aaf36212120c936952c1742d03d30Chris Banes child.post(new Runnable() { 1234ea004a01f44aaf36212120c936952c1742d03d30Chris Banes @Override 1235ea004a01f44aaf36212120c936952c1742d03d30Chris Banes public void run() { 1236ea004a01f44aaf36212120c936952c1742d03d30Chris Banes child.requestLayout(); 1237ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1238ea004a01f44aaf36212120c936952c1742d03d30Chris Banes }); 1239ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return false; 1240ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1241ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 12426ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes if (ViewCompat.getFitsSystemWindows(appBar)) { 12436ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // If the AppBarLayout is fitting system windows then we need to also, 12446ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes // otherwise we'll get CoL's compatible layout functionality 12456ba61c5c79fe025036593c9daf79cb65299bb0b6Chris Banes ViewCompat.setFitsSystemWindows(child, true); 1246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 124855f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes int availableHeight = MeasureSpec.getSize(parentHeightMeasureSpec); 124955f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes if (availableHeight == 0) { 125055f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes // If the measure spec doesn't specify a size, use the current height 125155f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes availableHeight = parent.getHeight(); 125255f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes } 1253ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 125455f3d29c12610da3574c4f488fbc3a1c7ca790f1Chris Banes final int height = availableHeight - appBar.getMeasuredHeight() 1255f57cd2fd5059c43d77bdfec57edfd5a217533103Chris Banes + appBar.getTotalScrollRange(); 1256a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 1257d6c56d929f7975eb6d80116e915ff5515a294c41Chris Banes childLpHeight == LayoutParams.MATCH_PARENT 1258d6c56d929f7975eb6d80116e915ff5515a294c41Chris Banes ? MeasureSpec.EXACTLY 1259d6c56d929f7975eb6d80116e915ff5515a294c41Chris Banes : MeasureSpec.AT_MOST); 1260a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Now measure the scrolling child with the correct height 1262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes parent.onMeasureChild(child, parentWidthMeasureSpec, 1263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes widthUsed, heightMeasureSpec, heightUsed); 1264a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1265a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return true; 1266a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1267a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 1269a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1270a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 1272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onDependentViewChanged(CoordinatorLayout parent, View child, 1273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View dependency) { 1274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final CoordinatorLayout.Behavior behavior = 1275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); 1276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (behavior instanceof Behavior) { 1277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Offset the child so that it is below the app-bar (with any overlap) 1278ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int offset = ((Behavior) behavior).getTopBottomOffsetForScrollingSibling(); 1279ea004a01f44aaf36212120c936952c1742d03d30Chris Banes setTopAndBottomOffset(dependency.getHeight() + offset 1280ea004a01f44aaf36212120c936952c1742d03d30Chris Banes - getOverlapForOffset(dependency, offset)); 1281ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1282ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return false; 1283ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1284ea004a01f44aaf36212120c936952c1742d03d30Chris Banes 1285ea004a01f44aaf36212120c936952c1742d03d30Chris Banes private int getOverlapForOffset(final View dependency, final int offset) { 1286ea004a01f44aaf36212120c936952c1742d03d30Chris Banes if (mOverlayTop != 0 && dependency instanceof AppBarLayout) { 1287ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final AppBarLayout abl = (AppBarLayout) dependency; 1288ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int totalScrollRange = abl.getTotalScrollRange(); 1289ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int preScrollDown = abl.getDownNestedPreScrollRange(); 1290a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1291ea004a01f44aaf36212120c936952c1742d03d30Chris Banes if (preScrollDown != 0 && (totalScrollRange + offset) <= preScrollDown) { 1292ea004a01f44aaf36212120c936952c1742d03d30Chris Banes // If we're in a pre-scroll down. Don't use the offset at all. 1293ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return 0; 1294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 1295ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final int availScrollRange = totalScrollRange - preScrollDown; 1296ea004a01f44aaf36212120c936952c1742d03d30Chris Banes if (availScrollRange == 0) { 1297ea004a01f44aaf36212120c936952c1742d03d30Chris Banes // Else we'll use a interpolated ratio of the overlap, depending on offset 1298ea004a01f44aaf36212120c936952c1742d03d30Chris Banes final float percScrolled = offset / (float) availScrollRange; 1299ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return MathUtils.constrain( 1300ea004a01f44aaf36212120c936952c1742d03d30Chris Banes Math.round((1f + percScrolled) * mOverlayTop), 0, mOverlayTop); 1301ea004a01f44aaf36212120c936952c1742d03d30Chris Banes } 1302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1304ea004a01f44aaf36212120c936952c1742d03d30Chris Banes return mOverlayTop; 1305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 1308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the distance that this view should overlap any {@link AppBarLayout}. 1309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 1310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param overlayTop the distance in px 1311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 1312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOverlayTop(int overlayTop) { 1313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = overlayTop; 1314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 1317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the distance that this view should overlap any {@link AppBarLayout}. 1318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 1319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getOverlayTop() { 1320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mOverlayTop; 1321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 1323a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static AppBarLayout findFirstAppBarLayout(List<View> views) { 1324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = views.size(); i < z; i++) { 1325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View view = views.get(i); 1326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (view instanceof AppBarLayout) { 1327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return (AppBarLayout) view; 1328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return null; 1331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 1333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes} 1334