AppBarLayout.java revision 631f64ec9c6255f38a7f746d7949b6a537c1180f
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; 2150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport android.support.annotation.IntDef; 22a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.design.R; 23a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat; 24631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport android.support.v4.widget.ScrollerCompat; 25a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet; 26a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View; 27a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup; 28a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator; 29a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout; 30a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 3150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.Retention; 3250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.RetentionPolicy; 33631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.lang.ref.WeakReference; 34631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.ArrayList; 35631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banesimport java.util.Iterator; 36a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List; 37a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 38a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/** 39a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of 40631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * material design's app bar concept, namely scrolling gestures. 41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through 43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute: 44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}. 45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}. 48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will 49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work. 50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to. The binding is done through 52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the {@link ScrollingViewBehavior} beahior class, meaning that you should set your scrolling 53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view's behavior to be an instance of {@link ScrollingViewBehavior}. A string resource containing 54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the full class name is available. 55a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 56a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre> 57a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.CoordinatorLayout 58a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:android="http://schemas.android.com/apk/res/android" 59a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:app="http://schemas.android.com/apk/res-auto" 60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent"> 62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v4.widget.NestedScrollView 64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent" 66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_behavior="@string/appbar_scrolling_view_behavior"> 67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <!-- Your scrolling content --> 69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.v4.widget.NestedScrollView> 71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.AppBarLayout 73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="wrap_content" 74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent"> 75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v7.widget.Toolbar 77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.TabLayout 81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.AppBarLayout> 85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.CoordinatorLayout> 87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre> 88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar"> 90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a> 91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class) 93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout { 94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 96631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical 97631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * offset changes. 98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 99631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public interface OnOffsetChangedListener { 100a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows 102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * child views to implement custom behavior based on the offset (for instance pinning a 103a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view at a certain y value). 104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 105631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param appBarLayout the {@link AppBarLayout} which offset has changed 10650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px 107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 108631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset); 109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static final int INVALID_SCROLL_RANGE = -1; 112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mTotalScrollRange = INVALID_SCROLL_RANGE; 114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownPreScrollRange = INVALID_SCROLL_RANGE; 115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownScrollRange = INVALID_SCROLL_RANGE; 116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 117a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes boolean mHaveChildWithInterpolator; 118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 11950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private float mTargetElevation; 12050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 121631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private final List<WeakReference<OnOffsetChangedListener>> mListeners; 122631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context) { 124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes this(context, null); 125a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 126a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 127a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context, AttributeSet attrs) { 128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 129a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setOrientation(VERTICAL); 13050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 13150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout, 13250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 0, R.style.Widget_Design_AppBarLayout); 13350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0); 13481520564f3dd783136e025174021ba4eabd6ff3cChris Banes setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background)); 13550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes a.recycle(); 13650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 13750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Use the bounds view outline provider so that we cast a shadow, even without a background 13850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewUtils.setBoundsViewOutlineProvider(this); 139631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 140631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mListeners = new ArrayList<>(); 141631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 142631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.setElevation(this, mTargetElevation); 143631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 144631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 145631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 146631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Add a listener that will be called when the offset of this {@link AppBarLayout} changes. 147631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 148631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener The listener that will be called when the offset changes.] 149631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 150631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @see #removeOnOffsetChangedListener(OnOffsetChangedListener) 151631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 152631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void addOnOffsetChangedListener(OnOffsetChangedListener listener) { 153631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes for (int i = 0, z = mListeners.size(); i < z; i++) { 154631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final WeakReference<OnOffsetChangedListener> ref = mListeners.get(i); 155631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (ref != null && ref.get() == listener) { 156631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Listener already added 157631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return; 158631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 159631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 160631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mListeners.add(new WeakReference<>(listener)); 161631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 162631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 163631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 164631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Remove the previously added {@link OnOffsetChangedListener}. 165631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 166631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param listener the listener to remove. 167631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 168631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) { 169631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final Iterator<WeakReference<OnOffsetChangedListener>> i = mListeners.iterator(); 170631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes while (i.hasNext()) { 171631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final WeakReference<OnOffsetChangedListener> ref = i.next(); 172631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final OnOffsetChangedListener item = ref.get(); 173631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (item == listener || item == null) { 174631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If the item is null, or is our given listener, remove 175631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes i.remove(); 176631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 177631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 178a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 179a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 180a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 181a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected void onLayout(boolean changed, int l, int t, int r, int b) { 182a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.onLayout(changed, l, t, r, b); 183a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 184a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Invalidate the scroll ranges 185a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 186a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 187a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 188a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 189a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = false; 190a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 191a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 192a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 193a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 194a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 195a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = true; 197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 198a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 199a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 200a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 201a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOrientation(int orientation) { 204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (orientation != VERTICAL) { 205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes throw new IllegalArgumentException("AppBarLayout is always vertical and does" 206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes + " not support horizontal orientation"); 207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.setOrientation(orientation); 209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return p instanceof LayoutParams; 214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 216a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 217a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateDefaultLayoutParams() { 218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams generateLayoutParams(AttributeSet attrs) { 223a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(getContext(), attrs); 224a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (p instanceof LinearLayout.LayoutParams) { 229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((LinearLayout.LayoutParams) p); 230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (p instanceof MarginLayoutParams) { 231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((MarginLayoutParams) p); 232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(p); 234a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 235a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasChildWithInterpolator() { 237a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mHaveChildWithInterpolator; 238a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 239a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 240a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 241a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range of any children 242a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 243a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @return the scroll range in px 244a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 245a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getTotalScrollRange() { 246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mTotalScrollRange != INVALID_SCROLL_RANGE) { 247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange; 248a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 249a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 250a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 251a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 252a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 253a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 254a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 255a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 256a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 258a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 259a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 260a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 264a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 265a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We also return the range straight away since later views can't scroll beneath 266a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us 267a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return range - ViewCompat.getMinimumHeight(child); 268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 269a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 270a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange = range; 276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasScrollableChildren() { 279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return getTotalScrollRange() != 0; 280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 283a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling up from a nested pre-scroll. 284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getUpNestedPreScrollRange() { 28650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return getTotalScrollRange(); 287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 290a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested pre-scroll. 291a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 292a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedPreScrollRange() { 293a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownPreScrollRange != INVALID_SCROLL_RANGE) { 294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange; 296a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 301a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) { 308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // The view has the quick return flag combination... 309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) { 310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If they're set to enter collapsed, use the minimum height 311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += ViewCompat.getMinimumHeight(child); 312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Else use the full height 314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (range > 0) { 317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we've hit an non-quick return scrollable view, and we've already hit a 318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // quick return view, return now 319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange = range; 323a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested scroll. 327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedScrollRange() { 329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownScrollRange != INVALID_SCROLL_RANGE) { 330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange; 332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing exit scroll, we to take the collapsed height into account. 350a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We also return the range straight away since later views can't scroll 351a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // beneath us 352a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return range - ViewCompat.getMinimumHeight(child); 353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange = range; 361a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 36350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int getMinimumHeightForVisibleOverlappingContent() { 36450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int minHeight = ViewCompat.getMinimumHeight(this); 36550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (minHeight != 0) { 36650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If this layout has a min height, use it (doubled) 36750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return minHeight * 2; 36850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 36950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 37050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Otherwise, we'll use twice the min height of our last child 37150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childCount = getChildCount(); 37250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return childCount >= 1 37350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2 37450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes : 0; 37550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 37650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 37750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** 378631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Set the elevation value to use when this {@link AppBarLayout} should be elevated 379631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * above content. 380631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * <p> 381631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * This method does not do anything itself. A typical use for this method is called from within 382631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * an {@link OnOffsetChangedListener} when the offset has changed in such a way to require an 383631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * elevation change. 384631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 385631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @param elevation the elevation value to use. 386631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * 387631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * @see ViewCompat#setElevation(View, float) 38850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes */ 389631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void setTargetElevation(float elevation) { 390631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mTargetElevation = elevation; 391631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 392631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 393631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes /** 394631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * Returns the elevation value to use when this {@link AppBarLayout} should be elevated 395631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes * above content. 396631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes */ 397631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public float getTargetElevation() { 39850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return mTargetElevation; 39950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 40050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class LayoutParams extends LinearLayout.LayoutParams { 40250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 40350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** @hide */ 40450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @IntDef(flag=true, value={ 40550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_SCROLL, 40650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_EXIT_UNTIL_COLLAPSED, 40750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS, 40850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED 40950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes }) 41050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Retention(RetentionPolicy.SOURCE) 41150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public @interface ScrollFlags {} 41250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The view will be scroll in direct relation to scroll events. This flag needs to be 415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * set for any of the other flags to take effect. If any sibling views 416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * before this one do not have this flag, then this value has no effect. 417a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_SCROLL = 0x1; 419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 420a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 421a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When exiting (scrolling off screen) the view will be scrolled until it is 422a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 'collapsed'. The collapsed height is defined by the view's minimum height. 423a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 424a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 425a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 426a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 427a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2; 428a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 429a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 430a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When entering (scrolling on screen) the view will scroll on any downwards 431a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll event, regardless of whether the scrolling view is also scrolling. This 432a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * is commonly referred to as the 'quick return' pattern. 433a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 434a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4; 435a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 436a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 437a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * An additional flag for 'enterAlways' which modifies the returning view to 438a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * only initially scroll back to it's collapsed height. Once the scrolling view has 439a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * reached the end of it's scroll range, the remainder of this view will be scrolled 440a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * into view. The collapsed height is defined by the view's minimum height. 441a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 442a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 443a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 444a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8; 446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Internal flag which allows quick checking of 'quick return' 449a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS; 451a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 452a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int mScrollFlags = SCROLL_FLAG_SCROLL; 453a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Interpolator mScrollInterpolator; 454a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 455a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(Context c, AttributeSet attrs) { 456a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(c, attrs); 457a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams); 458a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0); 459a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) { 460a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int resId = a.getResourceId( 461a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0); 462a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator( 463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes c, resId); 464a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 465a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 466a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 467a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 468a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height) { 469a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height); 470a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 471a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 472a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height, float weight) { 473a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height, weight); 474a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 475a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 476a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(ViewGroup.LayoutParams p) { 477a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(p); 478a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 479a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 480a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(MarginLayoutParams source) { 481a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 482a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 483a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 484a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LinearLayout.LayoutParams source) { 485a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 486a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 487a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 488a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LayoutParams source) { 489a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 490a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = source.mScrollFlags; 491a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = source.mScrollInterpolator; 492a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 493a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 494a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 495a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the scrolling flags. 496a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 497a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL}, 498a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS} 499a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * and {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED}. 500a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 501a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollFlags() 502a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 503a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags 504a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 50550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void setScrollFlags(@ScrollFlags int flags) { 506a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = flags; 507a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 508a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the scrolling flags. 511a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 512a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollFlags(int) 513a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 514a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags 515a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 51650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @ScrollFlags 517a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getScrollFlags() { 518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollFlags; 519a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 520a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 521a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 522a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the interpolator to when scrolling the view associated with this 523a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. 524a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 525a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling. 526a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 527a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator 528a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollInterpolator() 529a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 530a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setScrollInterpolator(Interpolator interpolator) { 531a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = interpolator; 532a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 533a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 534a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 535a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the {@link Interpolator} being used for scrolling the view associated with this 536a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling. 537a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 538a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator 539a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollInterpolator(Interpolator) 540a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 541a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Interpolator getScrollInterpolator() { 542a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollInterpolator; 543a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 544a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 545a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 546a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 547a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested 548a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll handling with offsetting. 549a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 550a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class Behavior extends ViewOffsetBehavior<AppBarLayout> { 551631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private int mLogicalOffsetTop; 552a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 55350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private boolean mSkipNestedPreScroll; 554631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private Runnable mFlingRunnable; 555631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private ScrollerCompat mScroller; 556631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 557631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private ValueAnimatorCompat mAnimator; 55850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 559a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior() {} 560a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 561a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior(Context context, AttributeSet attrs) { 562a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 563a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 564a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 565a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 567a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View directTargetChild, View target, int nestedScrollAxes) { 568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Return true if we're nested scrolling vertically and we have scrollable children 569631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 570a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes && child.hasScrollableChildren(); 571631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 572631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (started && mAnimator != null) { 573631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Cancel any offset animation 574631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.cancel(); 575631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 576631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 577631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return started; 578a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 579a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 580a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 581a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 582a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dx, int dy, int[] consumed) { 58350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy != 0 && !mSkipNestedPreScroll) { 58450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int min, max; 58550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy < 0) { 58650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling down 58750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getTotalScrollRange(); 58850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = min + child.getDownNestedPreScrollRange(); 58950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 59050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling up 59150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getUpNestedPreScrollRange(); 59250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = 0; 59350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 59450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes consumed[1] = scroll(coordinatorLayout, child, dy, min, max); 595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 596a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 600a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dxConsumed, int dyConsumed, 601a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int dxUnconsumed, int dyUnconsumed) { 60250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dyUnconsumed < 0) { 60350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If the scrolling view is scrolling down but not consuming, it's probably be at 60450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // the top of it's content 60550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes scroll(coordinatorLayout, child, dyUnconsumed, 60650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes -child.getDownNestedScrollRange(), 0); 60750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Set the expanding flag so that onNestedPreScroll doesn't handle any events 60850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = true; 60950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 61050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // As we're no longer handling nested scrolls, reset the skip flag 61150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 61350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 614a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 61550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 61650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 61750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes View target) { 61850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Reset the skip flag 61950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 62050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 62150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 62250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 623631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public boolean onNestedFling(final CoordinatorLayout coordinatorLayout, 624631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final AppBarLayout child, View target, float velocityX, float velocityY, 625631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes boolean consumed) { 626631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (!consumed) { 627631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // It has been consumed so let's fling ourselves 628631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return fling(coordinatorLayout, child, -child.getTotalScrollRange(), 0, -velocityY); 629631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 630631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're scrolling up and the child also consumed the fling. We'll fake scroll 631631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // upto our 'collapsed' offset 632631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes int targetScroll; 633631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (velocityY < 0) { 634631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling down 635631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes targetScroll = -child.getTotalScrollRange() 636631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes + child.getDownNestedPreScrollRange(); 637631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 638631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (getTopBottomOffsetForScrollingSibling() > targetScroll) { 639631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're currently expanded more than the target scroll, we'll return false 640631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // now. This is so that we don't 'scroll' the wrong way. 641631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 642631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 643631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 644631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // We're scrolling up 645631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes targetScroll = -child.getUpNestedPreScrollRange(); 646631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 647631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (getTopBottomOffsetForScrollingSibling() < targetScroll) { 648631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we're currently expanded less than the target scroll, we'll return 649631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // false now. This is so that we don't 'scroll' the wrong way. 650631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 651631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 652631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 653631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 654631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mLogicalOffsetTop != targetScroll) { 655631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes animateOffsetTo(coordinatorLayout, child, targetScroll); 656631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return true; 657631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 658631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 659631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 660631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 661631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 662631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 663631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private void animateOffsetTo(final CoordinatorLayout coordinatorLayout, 664631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final AppBarLayout child, int offset) { 665631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mAnimator == null) { 666631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator = ViewUtils.createAnimator(); 667631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR); 668631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() { 669631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 670631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void onAnimationUpdate(ValueAnimatorCompat animator) { 671631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes setAppBarTopBottomOffset(coordinatorLayout, child, 672631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes animator.getAnimatedIntValue()); 673631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 674631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes }); 675631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 676631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.cancel(); 677631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 678631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 679631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.setIntValues(getTopBottomOffsetForScrollingSibling(), offset); 680631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mAnimator.start(); 681631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 682631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 683631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private boolean fling(CoordinatorLayout coordinatorLayout, AppBarLayout layout, int minOffset, 684631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes int maxOffset, float velocityY) { 685631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mFlingRunnable != null) { 686631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes layout.removeCallbacks(mFlingRunnable); 687631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 688631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 689631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mScroller == null) { 690631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mScroller = ScrollerCompat.create(layout.getContext()); 691631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 692631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 693631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mScroller.fling( 694631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 0, mLogicalOffsetTop, // curr 695631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 0, Math.round(velocityY), // velocity. 696631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 0, 0, // x 697631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes minOffset, maxOffset); // y 698631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 699631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mScroller.computeScrollOffset()) { 700631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mFlingRunnable = new FlingRunnable(coordinatorLayout, layout); 701631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.postOnAnimation(layout, mFlingRunnable); 702631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return true; 703631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } else { 704631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mFlingRunnable = null; 705631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return false; 706631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 707631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 708631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 709631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private class FlingRunnable implements Runnable { 710631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private final CoordinatorLayout mParent; 711631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private final AppBarLayout mLayout; 712631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 713631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes FlingRunnable(CoordinatorLayout parent, AppBarLayout layout) { 714631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mParent = parent; 715631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mLayout = layout; 716631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 717631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 718631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 719631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes public void run() { 720631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (mLayout != null && mScroller != null && mScroller.computeScrollOffset()) { 721631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes setAppBarTopBottomOffset(mParent, mLayout, mScroller.getCurrY()); 722631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 723631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Post ourselves so that we run on the next animation 724631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes ViewCompat.postOnAnimation(mLayout, this); 725631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 726631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 727631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 728631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 729631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes @Override 73050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout child, 73150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int layoutDirection) { 73250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes boolean handled = super.onLayoutChild(parent, child, layoutDirection); 73350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 73450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Make sure we update the elevation 735631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes dispatchOffsetUpdates(child); 73650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 73750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return handled; 738a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 739a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 740a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout, 74150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int dy, int minOffset, int maxOffset) { 742a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, 743631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mLogicalOffsetTop - dy, minOffset, maxOffset); 744a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 745a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 746631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout, 747631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes AppBarLayout appBarLayout, int newOffset) { 748631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, newOffset, 749631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes Integer.MIN_VALUE, Integer.MAX_VALUE); 750631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes } 751631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 752631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout, 75350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) { 754631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final int curOffset = mLogicalOffsetTop; 755a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int consumed = 0; 756a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 757631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) { 758631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // If we have some scrolling range, and we're currently within the min and max 759631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // offsets, calculate a new offset 76050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset); 761a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 762a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (curOffset != newOffset) { 763a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes boolean offsetChanged = setTopAndBottomOffset( 764a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes appBarLayout.hasChildWithInterpolator() 765a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? interpolateOffset(appBarLayout, newOffset) 766a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : newOffset); 767631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes 768a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update how much dy we have consumed 769a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes consumed = curOffset - newOffset; 770a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update the stored sibling offset 771631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes mLogicalOffsetTop = newOffset; 772a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 773a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) { 774a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the offset hasn't changed and we're using an interpolated scroll 775a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // then we need to keep any dependent views updated. CoL will do this for 776a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us when we move, but we need to do it manually when we don't (as an 777a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // interpolated scroll may finish early). 778a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes coordinatorLayout.dispatchDependentViewsChanged(appBarLayout); 779a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 780a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 781631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Dispatch the updates to any listeners 782631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes dispatchOffsetUpdates(appBarLayout); 783a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 784a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 785a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 786a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return consumed; 787a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 788a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 789631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes private void dispatchOffsetUpdates(AppBarLayout layout) { 790631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final List<WeakReference<OnOffsetChangedListener>> listeners = layout.mListeners; 79150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 792631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // Iterate backwards through the list so that most recently added listeners 793631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes // get the first chance to decide 794631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes for (int i = 0, z = listeners.size(); i < z; i++) { 795631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final WeakReference<OnOffsetChangedListener> ref = listeners.get(i); 796631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes final OnOffsetChangedListener listener = ref != null ? ref.get() : null; 79750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 798631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes if (listener != null) { 799631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes listener.onOffsetChanged(layout, getTopAndBottomOffset()); 800a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 801a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 802a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 803a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 804a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int interpolateOffset(AppBarLayout layout, final int offset) { 805a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int absOffset = Math.abs(offset); 806a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 807a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 808a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = layout.getChildAt(i); 809a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 81050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 811a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 81250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (absOffset >= child.getTop() && absOffset <= child.getBottom()) { 813a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 81450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int childScrollableHeight = 0; 81550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int flags = childLp.getScrollFlags(); 81650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 81750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're set to scroll so add the child's height 81850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight += child.getHeight(); 81950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 82050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 82150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight -= ViewCompat.getMinimumHeight(child); 82250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 82350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 82450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 82550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childScrollableHeight > 0) { 82650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int offsetForView = absOffset - child.getTop(); 82750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int interpolatedDiff = Math.round(childScrollableHeight * 82850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes interpolator.getInterpolation( 82950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes offsetForView / (float) childScrollableHeight)); 83050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 83150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return Integer.signum(offset) * (child.getTop() + interpolatedDiff); 83250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 833a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 83450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 83550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If we get to here then the view on the offset isn't suitable for interpolated 83650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // scrolling. So break out of the loop 83750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes break; 838a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 839a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 840a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 841a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return offset; 842a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 843a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 844a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getTopBottomOffsetForScrollingSibling() { 845631f64ec9c6255f38a7f746d7949b6a537c1180fChris Banes return mLogicalOffsetTop; 846a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 847a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 848a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 849a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 850a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Behavior which should be used by {@link View}s which can scroll vertically and support 851a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * nested scrolling to automatically scroll any {@link AppBarLayout} siblings. 852a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 853a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class ScrollingViewBehavior extends ViewOffsetBehavior<View> { 854a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mOverlayTop; 855a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 856a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior() {} 857a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 858a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior(Context context, AttributeSet attrs) { 859a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 860a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 861a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, 862a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params); 863a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = a.getDimensionPixelSize( 864a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0); 865a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 866a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 867a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 868a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 869a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { 870a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We depend on any AppBarLayouts 871a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return dependency instanceof AppBarLayout; 872a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 873a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 874a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 875a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onMeasureChild(CoordinatorLayout parent, View child, 876a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentWidthMeasureSpec, int widthUsed, 877a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentHeightMeasureSpec, int heightUsed) { 878a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (child.getLayoutParams().height == LayoutParams.MATCH_PARENT) { 879a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the child's height is set to match_parent then it with it's maximum visible 880a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // visible height 881a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 882a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final List<View> dependencies = parent.getDependencies(child); 883a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dependencies.isEmpty()) { 884a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we don't have any dependencies, return false 885a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 886a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 887a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 888a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout appBar = findFirstAppBarLayout(dependencies); 889a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (appBar != null) { 890a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (appBar.getMeasuredWidth() == 0 || appBar.getMeasuredHeight() == 0) { 891a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the AppBar hasn't been measured yet, we need to do it now 892a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes parent.onMeasureChild(appBar, parentWidthMeasureSpec, 893a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes widthUsed, parentHeightMeasureSpec, heightUsed); 894a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 895a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 896a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollRange = appBar.getTotalScrollRange(); 897a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int height = MeasureSpec.getSize(parentHeightMeasureSpec) 898a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes - appBar.getMeasuredHeight() + scrollRange; 899a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 900a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes MeasureSpec.AT_MOST); 901a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 902a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Now measure the scrolling child with the correct height 903a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes parent.onMeasureChild(child, parentWidthMeasureSpec, 904a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes widthUsed, heightMeasureSpec, heightUsed); 905a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 906a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return true; 907a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 908a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 909a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 910a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 911a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 912a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 913a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onDependentViewChanged(CoordinatorLayout parent, View child, 914a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View dependency) { 915a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final CoordinatorLayout.Behavior behavior = 916a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); 917a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (behavior instanceof Behavior) { 918a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Offset the child so that it is below the app-bar (with any overlap) 919a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 920a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int appBarOffset = ((Behavior) behavior) 921a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes .getTopBottomOffsetForScrollingSibling(); 922a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int expandedMax = dependency.getHeight() - mOverlayTop; 923a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int collapsedMin = parent.getHeight() - child.getHeight(); 924a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 925a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mOverlayTop != 0 && dependency instanceof AppBarLayout) { 926a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we have an overlap top, and the dependency is an AppBarLayout, we control 927a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the offset ourselves based on the appbar's scroll progress. This is so that 928a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the scroll happens sequentially rather than linearly 929a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange(); 930a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin, 931a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Math.abs(appBarOffset) / (float) scrollRange)); 932a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 933a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setTopAndBottomOffset(MathUtils.constrain( 934a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes dependency.getHeight() - mOverlayTop + appBarOffset, 935a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes collapsedMin, expandedMax)); 936a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 937a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 938a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 939a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 940a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 941a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 942a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the distance that this view should overlap any {@link AppBarLayout}. 943a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 944a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param overlayTop the distance in px 945a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 946a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop 947a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 948a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOverlayTop(int overlayTop) { 949a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = overlayTop; 950a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 951a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 952a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 953a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the distance that this view should overlap any {@link AppBarLayout}. 954a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 955a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop 956a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 957a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getOverlayTop() { 958a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mOverlayTop; 959a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 960a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 961a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static AppBarLayout findFirstAppBarLayout(List<View> views) { 962a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = views.size(); i < z; i++) { 963a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View view = views.get(i); 964a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (view instanceof AppBarLayout) { 965a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return (AppBarLayout) view; 966a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 967a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 968a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return null; 969a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 970a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 971a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes} 972