AppBarLayout.java revision 81520564f3dd783136e025174021ba4eabd6ff3c
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; 24a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet; 25a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View; 26a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup; 27a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator; 28a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout; 29a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 3050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.Retention; 3150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banesimport java.lang.annotation.RetentionPolicy; 32a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List; 33a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 34a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/** 35a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of 36a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Material Design's App bar concept, namely scrolling gestures. 37a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 38a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through 39a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute: 40a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}. 41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}. 44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will 45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work. 46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to. The binding is done through 48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the {@link ScrollingViewBehavior} beahior class, meaning that you should set your scrolling 49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view's behavior to be an instance of {@link ScrollingViewBehavior}. A string resource containing 50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the full class name is available. 51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre> 53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.CoordinatorLayout 54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:android="http://schemas.android.com/apk/res/android" 55a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:app="http://schemas.android.com/apk/res-auto" 56a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 57a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent"> 58a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 59a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v4.widget.NestedScrollView 60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent" 62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_behavior="@string/appbar_scrolling_view_behavior"> 63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <!-- Your scrolling content --> 65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.v4.widget.NestedScrollView> 67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.AppBarLayout 69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="wrap_content" 70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent"> 71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v7.widget.Toolbar 73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.TabLayout 77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.AppBarLayout> 81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.CoordinatorLayout> 83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre> 84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar"> 86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a> 87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class) 89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout { 90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Interface which allows an implementing child {@link View} of this {@link AppBarLayout} to 93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * receive offset updates, and provide extra information. 94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public interface AppBarLayoutChild { 9650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 9750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** @hide */ 9850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @IntDef({ 9950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes STATE_ELEVATED_ABOVE, 10050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes STATE_ELEVATED_INLINE 10150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes }) 10250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Retention(RetentionPolicy.SOURCE) 10350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @interface ElevatedState {} 10450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 10550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** 10650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * The {@link AppBarLayout} should be elevated above any scrolling content, and this cast 10750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * a shadow. 10850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * 10950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * @see #onOffsetUpdate(int) 11050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes */ 11150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int STATE_ELEVATED_ABOVE = 1; 11250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 11350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** 11450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * The {@link AppBarLayout} should not be elevated above any scrolling content. 11550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * 11650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * @see #onOffsetUpdate(int) 11750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes */ 11850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int STATE_ELEVATED_INLINE = 0; 11950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows 122a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * child views to implement custom behavior based on the offset (for instance pinning a 123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view at a certain y value). 124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 12550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * <p>You can influence the elevation of the {@link AppBarLayout} by returning one of 12650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * {@link #STATE_ELEVATED_INLINE} or {@link #STATE_ELEVATED_ABOVE}. 12750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * 12850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px 12950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * 13050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * @return one of {@link #STATE_ELEVATED_INLINE} or {@link #STATE_ELEVATED_ABOVE}. 131a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 13250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @ElevatedState 13350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int onOffsetUpdate(int verticalOffset); 134a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 135a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 136a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static final int INVALID_SCROLL_RANGE = -1; 137a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 138a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mTotalScrollRange = INVALID_SCROLL_RANGE; 139a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownPreScrollRange = INVALID_SCROLL_RANGE; 140a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownScrollRange = INVALID_SCROLL_RANGE; 141a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 142a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes boolean mHaveChildWithInterpolator; 143a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 14450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private float mTargetElevation; 14550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 146a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context) { 147a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes this(context, null); 148a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 149a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 150a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context, AttributeSet attrs) { 151a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 152a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setOrientation(VERTICAL); 15350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 15450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout, 15550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 0, R.style.Widget_Design_AppBarLayout); 15650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0); 15781520564f3dd783136e025174021ba4eabd6ff3cChris Banes setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background)); 15850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes a.recycle(); 15950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 16050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Use the bounds view outline provider so that we cast a shadow, even without a background 16150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewUtils.setBoundsViewOutlineProvider(this); 162a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 163a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 164a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 165a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected void onLayout(boolean changed, int l, int t, int r, int b) { 166a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.onLayout(changed, l, t, r, b); 167a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 168a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Invalidate the scroll ranges 169a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 170a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 171a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 172a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 173a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = false; 174a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 175a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 176a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 177a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 178a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 179a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 180a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = true; 181a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 182a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 183a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 184a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 185a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 186a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 187a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOrientation(int orientation) { 188a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (orientation != VERTICAL) { 189a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes throw new IllegalArgumentException("AppBarLayout is always vertical and does" 190a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes + " not support horizontal orientation"); 191a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 192a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.setOrientation(orientation); 193a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 194a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 195a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return p instanceof LayoutParams; 198a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 199a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 200a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 201a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateDefaultLayoutParams() { 202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams generateLayoutParams(AttributeSet attrs) { 207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(getContext(), attrs); 208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (p instanceof LinearLayout.LayoutParams) { 213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((LinearLayout.LayoutParams) p); 214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (p instanceof MarginLayoutParams) { 215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((MarginLayoutParams) p); 216a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 217a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(p); 218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasChildWithInterpolator() { 221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mHaveChildWithInterpolator; 222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 223a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 224a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range of any children 226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @return the scroll range in px 228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getTotalScrollRange() { 230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mTotalScrollRange != INVALID_SCROLL_RANGE) { 231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange; 232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 234a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 235a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 237a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 238a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 239a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 240a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 241a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 242a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 243a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 244a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 245a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 248a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 249a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We also return the range straight away since later views can't scroll beneath 250a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us 251a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return range - ViewCompat.getMinimumHeight(child); 252a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 253a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 254a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 255a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 256a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 258a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 259a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange = range; 260a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasScrollableChildren() { 263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return getTotalScrollRange() != 0; 264a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 265a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 266a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 267a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling up from a nested pre-scroll. 268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 269a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getUpNestedPreScrollRange() { 27050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return getTotalScrollRange(); 271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested pre-scroll. 275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedPreScrollRange() { 277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownPreScrollRange != INVALID_SCROLL_RANGE) { 278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange; 280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 283a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 286a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 290a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 291a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) { 292a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // The view has the quick return flag combination... 293a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) { 294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If they're set to enter collapsed, use the minimum height 295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += ViewCompat.getMinimumHeight(child); 296a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Else use the full height 298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (range > 0) { 301a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we've hit an non-quick return scrollable view, and we've already hit a 302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // quick return view, return now 303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange = range; 307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested scroll. 311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedScrollRange() { 313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownScrollRange != INVALID_SCROLL_RANGE) { 314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange; 316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 323a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing exit scroll, we to take the collapsed height into account. 334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We also return the range straight away since later views can't scroll 335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // beneath us 336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return range - ViewCompat.getMinimumHeight(child); 337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange = range; 345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 34750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int getMinimumHeightForVisibleOverlappingContent() { 34850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int minHeight = ViewCompat.getMinimumHeight(this); 34950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (minHeight != 0) { 35050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If this layout has a min height, use it (doubled) 35150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return minHeight * 2; 35250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 35350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 35450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Otherwise, we'll use twice the min height of our last child 35550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childCount = getChildCount(); 35650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return childCount >= 1 35750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2 35850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes : 0; 35950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 36050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 36150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** 36250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * The elevation value to use when {@link AppBarLayout} is elevated above content. 36350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes */ 36450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final float getTargetElevation() { 36550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return mTargetElevation; 36650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 36750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 368a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class LayoutParams extends LinearLayout.LayoutParams { 36950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 37050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** @hide */ 37150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @IntDef(flag=true, value={ 37250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_SCROLL, 37350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_EXIT_UNTIL_COLLAPSED, 37450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS, 37550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED 37650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes }) 37750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Retention(RetentionPolicy.SOURCE) 37850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public @interface ScrollFlags {} 37950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 380a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The view will be scroll in direct relation to scroll events. This flag needs to be 382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * set for any of the other flags to take effect. If any sibling views 383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * before this one do not have this flag, then this value has no effect. 384a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 385a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_SCROLL = 0x1; 386a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 387a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 388a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When exiting (scrolling off screen) the view will be scrolled until it is 389a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 'collapsed'. The collapsed height is defined by the view's minimum height. 390a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 391a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 392a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 393a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 394a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2; 395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 397a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When entering (scrolling on screen) the view will scroll on any downwards 398a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll event, regardless of whether the scrolling view is also scrolling. This 399a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * is commonly referred to as the 'quick return' pattern. 400a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4; 402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 404a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * An additional flag for 'enterAlways' which modifies the returning view to 405a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * only initially scroll back to it's collapsed height. Once the scrolling view has 406a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * reached the end of it's scroll range, the remainder of this view will be scrolled 407a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * into view. The collapsed height is defined by the view's minimum height. 408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8; 413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Internal flag which allows quick checking of 'quick return' 416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 417a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS; 418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int mScrollFlags = SCROLL_FLAG_SCROLL; 420a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Interpolator mScrollInterpolator; 421a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 422a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(Context c, AttributeSet attrs) { 423a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(c, attrs); 424a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams); 425a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0); 426a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) { 427a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int resId = a.getResourceId( 428a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0); 429a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator( 430a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes c, resId); 431a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 432a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 433a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 434a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 435a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height) { 436a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height); 437a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 438a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 439a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height, float weight) { 440a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height, weight); 441a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 442a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 443a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(ViewGroup.LayoutParams p) { 444a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(p); 445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(MarginLayoutParams source) { 448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 449a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 451a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LinearLayout.LayoutParams source) { 452a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 453a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 454a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 455a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LayoutParams source) { 456a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 457a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = source.mScrollFlags; 458a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = source.mScrollInterpolator; 459a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 460a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 461a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 462a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the scrolling flags. 463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 464a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL}, 465a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS} 466a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * and {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED}. 467a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 468a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollFlags() 469a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 470a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags 471a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 47250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void setScrollFlags(@ScrollFlags int flags) { 473a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = flags; 474a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 475a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 476a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 477a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the scrolling flags. 478a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 479a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollFlags(int) 480a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 481a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags 482a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 48350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @ScrollFlags 484a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getScrollFlags() { 485a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollFlags; 486a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 487a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 488a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 489a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the interpolator to when scrolling the view associated with this 490a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. 491a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 492a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling. 493a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 494a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator 495a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollInterpolator() 496a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 497a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setScrollInterpolator(Interpolator interpolator) { 498a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = interpolator; 499a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 500a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 501a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 502a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the {@link Interpolator} being used for scrolling the view associated with this 503a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling. 504a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 505a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator 506a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollInterpolator(Interpolator) 507a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 508a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Interpolator getScrollInterpolator() { 509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollInterpolator; 510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 511a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 512a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 513a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 514a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested 515a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll handling with offsetting. 516a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 517a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class Behavior extends ViewOffsetBehavior<AppBarLayout> { 518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mSiblingOffsetTop; 519a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 52050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private boolean mSkipNestedPreScroll; 52150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 522a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior() {} 523a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 524a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior(Context context, AttributeSet attrs) { 525a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 526a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 527a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 528a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 529a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 530a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View directTargetChild, View target, int nestedScrollAxes) { 531a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Return true if we're nested scrolling vertically and we have scrollable children 532a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 533a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes && child.hasScrollableChildren(); 534a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 535a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 536a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 537a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 538a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dx, int dy, int[] consumed) { 53950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy != 0 && !mSkipNestedPreScroll) { 54050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int min, max; 54150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy < 0) { 54250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling down 54350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getTotalScrollRange(); 54450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = min + child.getDownNestedPreScrollRange(); 54550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 54650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling up 54750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getUpNestedPreScrollRange(); 54850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = 0; 54950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 55050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes consumed[1] = scroll(coordinatorLayout, child, dy, min, max); 551a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 552a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 553a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 554a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 555a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 556a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dxConsumed, int dyConsumed, 557a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int dxUnconsumed, int dyUnconsumed) { 55850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dyUnconsumed < 0) { 55950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If the scrolling view is scrolling down but not consuming, it's probably be at 56050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // the top of it's content 56150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes scroll(coordinatorLayout, child, dyUnconsumed, 56250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes -child.getDownNestedScrollRange(), 0); 56350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Set the expanding flag so that onNestedPreScroll doesn't handle any events 56450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = true; 56550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 56650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // As we're no longer handling nested scrolls, reset the skip flag 56750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 56950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 570a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 57150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 57250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 57350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes View target) { 57450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Reset the skip flag 57550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 57650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 57750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 57850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 57950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout child, 58050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int layoutDirection) { 58150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes boolean handled = super.onLayoutChild(parent, child, layoutDirection); 58250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 58350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Make sure we update the elevation 58450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int elevationState = dispatchOffsetUpdates(child); 58550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes checkElevation(child, getTopAndBottomOffset(), elevationState); 58650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 58750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return handled; 588a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 589a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 590a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout, 59150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int dy, int minOffset, int maxOffset) { 592a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, 59350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSiblingOffsetTop - dy, minOffset, maxOffset); 594a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 596a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout, 59750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) { 598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int curOffset = mSiblingOffsetTop; 599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int consumed = 0; 600a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 60150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (minOffset != 0) { 60250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset); 603a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 604a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (curOffset != newOffset) { 605a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes boolean offsetChanged = setTopAndBottomOffset( 606a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes appBarLayout.hasChildWithInterpolator() 607a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? interpolateOffset(appBarLayout, newOffset) 608a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : newOffset); 609a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update how much dy we have consumed 610a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes consumed = curOffset - newOffset; 611a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update the stored sibling offset 612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mSiblingOffsetTop = newOffset; 613a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 614a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) { 615a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the offset hasn't changed and we're using an interpolated scroll 616a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // then we need to keep any dependent views updated. CoL will do this for 617a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us when we move, but we need to do it manually when we don't (as an 618a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // interpolated scroll may finish early). 619a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes coordinatorLayout.dispatchDependentViewsChanged(appBarLayout); 620a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 621a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 62250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Dispatch the updates to any AppBarLayoutChild children 62350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childState = dispatchOffsetUpdates(appBarLayout); 62450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes checkElevation(appBarLayout, newOffset, childState); 625a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 626a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 627a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 628a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return consumed; 629a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 630a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 63150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private void checkElevation(AppBarLayout appBarLayout, int offset, int childState) { 63250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (appBarLayout.getHeight() + offset == 0) { 63350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If we're not visible, clear out the elevation 63450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewCompat.setElevation(appBarLayout, 0f); 63550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 63650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childState == AppBarLayoutChild.STATE_ELEVATED_ABOVE) { 63750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewCompat.setElevation(appBarLayout, appBarLayout.getTargetElevation()); 63850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 63950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewCompat.setElevation(appBarLayout, 0f); 64050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 64150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 64250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 64350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 64450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private int dispatchOffsetUpdates(AppBarLayout layout) { 645a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 646a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View child = layout.getChildAt(i); 647a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (child instanceof AppBarLayoutChild) { 64850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childState = ((AppBarLayoutChild) child) 64950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes .onOffsetUpdate(getTopAndBottomOffset()); 65050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 65150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childState == AppBarLayoutChild.STATE_ELEVATED_INLINE) { 65250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return childState; 65350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 654a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 655a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 65650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 65750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return AppBarLayoutChild.STATE_ELEVATED_ABOVE; 658a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 659a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 660a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int interpolateOffset(AppBarLayout layout, final int offset) { 661a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int absOffset = Math.abs(offset); 662a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 663a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 664a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = layout.getChildAt(i); 665a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 66650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 667a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 66850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (absOffset >= child.getTop() && absOffset <= child.getBottom()) { 669a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 67050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int childScrollableHeight = 0; 67150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int flags = childLp.getScrollFlags(); 67250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 67350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're set to scroll so add the child's height 67450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight += child.getHeight(); 67550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 67650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 67750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight -= ViewCompat.getMinimumHeight(child); 67850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 67950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 68050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 68150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childScrollableHeight > 0) { 68250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int offsetForView = absOffset - child.getTop(); 68350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int interpolatedDiff = Math.round(childScrollableHeight * 68450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes interpolator.getInterpolation( 68550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes offsetForView / (float) childScrollableHeight)); 68650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 68750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return Integer.signum(offset) * (child.getTop() + interpolatedDiff); 68850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 689a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 69050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 69150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If we get to here then the view on the offset isn't suitable for interpolated 69250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // scrolling. So break out of the loop 69350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes break; 694a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 695a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 696a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 697a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return offset; 698a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 699a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 700a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getTopBottomOffsetForScrollingSibling() { 701a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mSiblingOffsetTop; 702a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 703a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 704a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 705a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 706a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Behavior which should be used by {@link View}s which can scroll vertically and support 707a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * nested scrolling to automatically scroll any {@link AppBarLayout} siblings. 708a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 709a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class ScrollingViewBehavior extends ViewOffsetBehavior<View> { 710a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mOverlayTop; 711a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 712a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior() {} 713a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 714a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior(Context context, AttributeSet attrs) { 715a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 716a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 717a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, 718a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params); 719a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = a.getDimensionPixelSize( 720a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0); 721a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 722a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 723a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 724a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 725a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { 726a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We depend on any AppBarLayouts 727a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return dependency instanceof AppBarLayout; 728a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 729a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 730a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 731a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onMeasureChild(CoordinatorLayout parent, View child, 732a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentWidthMeasureSpec, int widthUsed, 733a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentHeightMeasureSpec, int heightUsed) { 734a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (child.getLayoutParams().height == LayoutParams.MATCH_PARENT) { 735a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the child's height is set to match_parent then it with it's maximum visible 736a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // visible height 737a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 738a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final List<View> dependencies = parent.getDependencies(child); 739a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dependencies.isEmpty()) { 740a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we don't have any dependencies, return false 741a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 742a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 743a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 744a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout appBar = findFirstAppBarLayout(dependencies); 745a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (appBar != null) { 746a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (appBar.getMeasuredWidth() == 0 || appBar.getMeasuredHeight() == 0) { 747a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the AppBar hasn't been measured yet, we need to do it now 748a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes parent.onMeasureChild(appBar, parentWidthMeasureSpec, 749a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes widthUsed, parentHeightMeasureSpec, heightUsed); 750a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 751a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 752a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollRange = appBar.getTotalScrollRange(); 753a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int height = MeasureSpec.getSize(parentHeightMeasureSpec) 754a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes - appBar.getMeasuredHeight() + scrollRange; 755a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 756a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes MeasureSpec.AT_MOST); 757a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 758a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Now measure the scrolling child with the correct height 759a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes parent.onMeasureChild(child, parentWidthMeasureSpec, 760a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes widthUsed, heightMeasureSpec, heightUsed); 761a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 762a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return true; 763a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 764a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 765a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 766a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 767a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 768a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 769a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onDependentViewChanged(CoordinatorLayout parent, View child, 770a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View dependency) { 771a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final CoordinatorLayout.Behavior behavior = 772a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); 773a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (behavior instanceof Behavior) { 774a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Offset the child so that it is below the app-bar (with any overlap) 775a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 776a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int appBarOffset = ((Behavior) behavior) 777a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes .getTopBottomOffsetForScrollingSibling(); 778a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int expandedMax = dependency.getHeight() - mOverlayTop; 779a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int collapsedMin = parent.getHeight() - child.getHeight(); 780a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 781a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mOverlayTop != 0 && dependency instanceof AppBarLayout) { 782a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we have an overlap top, and the dependency is an AppBarLayout, we control 783a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the offset ourselves based on the appbar's scroll progress. This is so that 784a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the scroll happens sequentially rather than linearly 785a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange(); 786a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin, 787a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Math.abs(appBarOffset) / (float) scrollRange)); 788a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 789a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setTopAndBottomOffset(MathUtils.constrain( 790a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes dependency.getHeight() - mOverlayTop + appBarOffset, 791a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes collapsedMin, expandedMax)); 792a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 793a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 794a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 795a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 796a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 797a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 798a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the distance that this view should overlap any {@link AppBarLayout}. 799a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 800a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param overlayTop the distance in px 801a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 802a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop 803a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 804a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOverlayTop(int overlayTop) { 805a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = overlayTop; 806a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 807a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 808a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 809a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the distance that this view should overlap any {@link AppBarLayout}. 810a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 811a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop 812a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 813a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getOverlayTop() { 814a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mOverlayTop; 815a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 816a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 817a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static AppBarLayout findFirstAppBarLayout(List<View> views) { 818a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = views.size(); i < z; i++) { 819a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View view = views.get(i); 820a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (view instanceof AppBarLayout) { 821a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return (AppBarLayout) view; 822a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 823a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 824a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return null; 825a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 826a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 827a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes} 828