AppBarLayout.java revision 50dfc33a565c4aefe6d5e844c93aa24a74cb80b3
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); 15750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes a.recycle(); 15850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 15950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Use the bounds view outline provider so that we cast a shadow, even without a background 16050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewUtils.setBoundsViewOutlineProvider(this); 161a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 162a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 163a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 164a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected void onLayout(boolean changed, int l, int t, int r, int b) { 165a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.onLayout(changed, l, t, r, b); 166a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 167a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Invalidate the scroll ranges 168a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 169a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 170a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 171a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 172a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = false; 173a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 174a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 175a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 176a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 177a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 178a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 179a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = true; 180a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 181a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 182a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 183a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 184a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 185a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 186a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOrientation(int orientation) { 187a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (orientation != VERTICAL) { 188a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes throw new IllegalArgumentException("AppBarLayout is always vertical and does" 189a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes + " not support horizontal orientation"); 190a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 191a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.setOrientation(orientation); 192a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 193a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 194a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 195a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return p instanceof LayoutParams; 197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 198a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 199a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 200a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateDefaultLayoutParams() { 201a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams generateLayoutParams(AttributeSet attrs) { 206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(getContext(), attrs); 207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (p instanceof LinearLayout.LayoutParams) { 212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((LinearLayout.LayoutParams) p); 213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (p instanceof MarginLayoutParams) { 214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((MarginLayoutParams) p); 215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 216a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(p); 217a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasChildWithInterpolator() { 220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mHaveChildWithInterpolator; 221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 223a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 224a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range of any children 225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @return the scroll range in px 227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getTotalScrollRange() { 229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mTotalScrollRange != INVALID_SCROLL_RANGE) { 230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange; 231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 234a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 235a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 237a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 238a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 239a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 240a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 241a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 242a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 243a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 244a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 245a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 248a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We also return the range straight away since later views can't scroll beneath 249a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us 250a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return range - ViewCompat.getMinimumHeight(child); 251a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 252a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 253a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 254a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 255a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 256a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 258a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange = range; 259a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 260a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasScrollableChildren() { 262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return getTotalScrollRange() != 0; 263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 264a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 265a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 266a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling up from a nested pre-scroll. 267a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getUpNestedPreScrollRange() { 26950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return getTotalScrollRange(); 270a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested pre-scroll. 274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedPreScrollRange() { 276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownPreScrollRange != INVALID_SCROLL_RANGE) { 277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange; 279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 283a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 286a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 290a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) { 291a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // The view has the quick return flag combination... 292a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) { 293a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If they're set to enter collapsed, use the minimum height 294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += ViewCompat.getMinimumHeight(child); 295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 296a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Else use the full height 297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (range > 0) { 300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we've hit an non-quick return scrollable view, and we've already hit a 301a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // quick return view, return now 302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange = range; 306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested scroll. 310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedScrollRange() { 312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownScrollRange != INVALID_SCROLL_RANGE) { 313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange; 315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 323a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing exit scroll, we to take the collapsed height into account. 333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We also return the range straight away since later views can't scroll 334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // beneath us 335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return range - ViewCompat.getMinimumHeight(child); 336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange = range; 344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 34650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int getMinimumHeightForVisibleOverlappingContent() { 34750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int minHeight = ViewCompat.getMinimumHeight(this); 34850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (minHeight != 0) { 34950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If this layout has a min height, use it (doubled) 35050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return minHeight * 2; 35150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 35250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 35350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Otherwise, we'll use twice the min height of our last child 35450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childCount = getChildCount(); 35550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return childCount >= 1 35650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2 35750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes : 0; 35850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 35950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 36050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** 36150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes * The elevation value to use when {@link AppBarLayout} is elevated above content. 36250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes */ 36350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final float getTargetElevation() { 36450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return mTargetElevation; 36550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 36650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class LayoutParams extends LinearLayout.LayoutParams { 36850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 36950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes /** @hide */ 37050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @IntDef(flag=true, value={ 37150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_SCROLL, 37250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_EXIT_UNTIL_COLLAPSED, 37350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS, 37450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED 37550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes }) 37650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Retention(RetentionPolicy.SOURCE) 37750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public @interface ScrollFlags {} 37850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 379a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 380a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The view will be scroll in direct relation to scroll events. This flag needs to be 381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * set for any of the other flags to take effect. If any sibling views 382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * before this one do not have this flag, then this value has no effect. 383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 384a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_SCROLL = 0x1; 385a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 386a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 387a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When exiting (scrolling off screen) the view will be scrolled until it is 388a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 'collapsed'. The collapsed height is defined by the view's minimum height. 389a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 390a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 391a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 392a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 393a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2; 394a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When entering (scrolling on screen) the view will scroll on any downwards 397a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll event, regardless of whether the scrolling view is also scrolling. This 398a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * is commonly referred to as the 'quick return' pattern. 399a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 400a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4; 401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * An additional flag for 'enterAlways' which modifies the returning view to 404a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * only initially scroll back to it's collapsed height. Once the scrolling view has 405a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * reached the end of it's scroll range, the remainder of this view will be scrolled 406a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * into view. The collapsed height is defined by the view's minimum height. 407a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8; 412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Internal flag which allows quick checking of 'quick return' 415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS; 417a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int mScrollFlags = SCROLL_FLAG_SCROLL; 419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Interpolator mScrollInterpolator; 420a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 421a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(Context c, AttributeSet attrs) { 422a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(c, attrs); 423a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams); 424a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0); 425a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) { 426a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int resId = a.getResourceId( 427a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0); 428a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator( 429a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes c, resId); 430a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 431a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 432a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 433a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 434a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height) { 435a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height); 436a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 437a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 438a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height, float weight) { 439a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height, weight); 440a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 441a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 442a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(ViewGroup.LayoutParams p) { 443a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(p); 444a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(MarginLayoutParams source) { 447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 449a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LinearLayout.LayoutParams source) { 451a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 452a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 453a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 454a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LayoutParams source) { 455a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 456a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = source.mScrollFlags; 457a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = source.mScrollInterpolator; 458a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 459a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 460a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 461a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the scrolling flags. 462a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL}, 464a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS} 465a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * and {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED}. 466a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 467a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollFlags() 468a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 469a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags 470a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 47150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void setScrollFlags(@ScrollFlags int flags) { 472a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = flags; 473a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 474a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 475a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 476a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the scrolling flags. 477a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 478a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollFlags(int) 479a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 480a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags 481a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 48250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @ScrollFlags 483a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getScrollFlags() { 484a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollFlags; 485a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 486a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 487a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 488a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the interpolator to when scrolling the view associated with this 489a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. 490a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 491a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling. 492a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 493a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator 494a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollInterpolator() 495a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 496a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setScrollInterpolator(Interpolator interpolator) { 497a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = interpolator; 498a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 499a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 500a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 501a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the {@link Interpolator} being used for scrolling the view associated with this 502a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling. 503a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 504a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator 505a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollInterpolator(Interpolator) 506a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 507a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Interpolator getScrollInterpolator() { 508a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollInterpolator; 509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 511a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 512a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 513a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested 514a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll handling with offsetting. 515a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 516a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class Behavior extends ViewOffsetBehavior<AppBarLayout> { 517a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mSiblingOffsetTop; 518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 51950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private boolean mSkipNestedPreScroll; 52050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 521a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior() {} 522a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 523a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior(Context context, AttributeSet attrs) { 524a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 525a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 526a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 527a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 528a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 529a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View directTargetChild, View target, int nestedScrollAxes) { 530a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Return true if we're nested scrolling vertically and we have scrollable children 531a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 532a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes && child.hasScrollableChildren(); 533a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 534a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 535a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 536a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 537a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dx, int dy, int[] consumed) { 53850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy != 0 && !mSkipNestedPreScroll) { 53950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int min, max; 54050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dy < 0) { 54150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling down 54250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getTotalScrollRange(); 54350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = min + child.getDownNestedPreScrollRange(); 54450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 54550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're scrolling up 54650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes min = -child.getUpNestedPreScrollRange(); 54750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes max = 0; 54850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 54950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes consumed[1] = scroll(coordinatorLayout, child, dy, min, max); 550a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 551a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 552a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 553a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 554a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 555a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dxConsumed, int dyConsumed, 556a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int dxUnconsumed, int dyUnconsumed) { 55750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (dyUnconsumed < 0) { 55850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If the scrolling view is scrolling down but not consuming, it's probably be at 55950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // the top of it's content 56050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes scroll(coordinatorLayout, child, dyUnconsumed, 56150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes -child.getDownNestedScrollRange(), 0); 56250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Set the expanding flag so that onNestedPreScroll doesn't handle any events 56350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = true; 56450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 56550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // As we're no longer handling nested scrolls, reset the skip flag 56650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 567a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 56850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 569a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 57050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 57150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 57250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes View target) { 57350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Reset the skip flag 57450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSkipNestedPreScroll = false; 57550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 57650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 57750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes @Override 57850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout child, 57950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int layoutDirection) { 58050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes boolean handled = super.onLayoutChild(parent, child, layoutDirection); 58150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 58250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Make sure we update the elevation 58350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int elevationState = dispatchOffsetUpdates(child); 58450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes checkElevation(child, getTopAndBottomOffset(), elevationState); 58550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 58650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return handled; 587a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 588a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 589a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout, 59050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int dy, int minOffset, int maxOffset) { 591a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, 59250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes mSiblingOffsetTop - dy, minOffset, maxOffset); 593a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 594a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout, 59650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) { 597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int curOffset = mSiblingOffsetTop; 598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int consumed = 0; 599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 60050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (minOffset != 0) { 60150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset); 602a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 603a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (curOffset != newOffset) { 604a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes boolean offsetChanged = setTopAndBottomOffset( 605a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes appBarLayout.hasChildWithInterpolator() 606a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? interpolateOffset(appBarLayout, newOffset) 607a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : newOffset); 608a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update how much dy we have consumed 609a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes consumed = curOffset - newOffset; 610a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update the stored sibling offset 611a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mSiblingOffsetTop = newOffset; 612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 613a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) { 614a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the offset hasn't changed and we're using an interpolated scroll 615a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // then we need to keep any dependent views updated. CoL will do this for 616a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us when we move, but we need to do it manually when we don't (as an 617a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // interpolated scroll may finish early). 618a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes coordinatorLayout.dispatchDependentViewsChanged(appBarLayout); 619a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 620a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 62150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // Dispatch the updates to any AppBarLayoutChild children 62250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childState = dispatchOffsetUpdates(appBarLayout); 62350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes checkElevation(appBarLayout, newOffset, childState); 624a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 625a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 626a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 627a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return consumed; 628a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 629a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 63050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private void checkElevation(AppBarLayout appBarLayout, int offset, int childState) { 63150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (appBarLayout.getHeight() + offset == 0) { 63250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If we're not visible, clear out the elevation 63350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewCompat.setElevation(appBarLayout, 0f); 63450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 63550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childState == AppBarLayoutChild.STATE_ELEVATED_ABOVE) { 63650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewCompat.setElevation(appBarLayout, appBarLayout.getTargetElevation()); 63750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } else { 63850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes ViewCompat.setElevation(appBarLayout, 0f); 63950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 64050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 64150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 64250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 64350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes private int dispatchOffsetUpdates(AppBarLayout layout) { 644a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 645a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View child = layout.getChildAt(i); 646a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (child instanceof AppBarLayoutChild) { 64750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int childState = ((AppBarLayoutChild) child) 64850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes .onOffsetUpdate(getTopAndBottomOffset()); 64950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 65050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childState == AppBarLayoutChild.STATE_ELEVATED_INLINE) { 65150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return childState; 65250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 653a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 654a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 65550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 65650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return AppBarLayoutChild.STATE_ELEVATED_ABOVE; 657a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 658a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 659a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int interpolateOffset(AppBarLayout layout, final int offset) { 660a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int absOffset = Math.abs(offset); 661a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 662a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 663a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = layout.getChildAt(i); 664a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 66550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 666a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 66750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (absOffset >= child.getTop() && absOffset <= child.getBottom()) { 668a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 66950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes int childScrollableHeight = 0; 67050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int flags = childLp.getScrollFlags(); 67150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 67250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // We're set to scroll so add the child's height 67350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight += child.getHeight(); 67450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 67550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 67650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes childScrollableHeight -= ViewCompat.getMinimumHeight(child); 67750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 67850dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 67950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 68050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes if (childScrollableHeight > 0) { 68150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int offsetForView = absOffset - child.getTop(); 68250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes final int interpolatedDiff = Math.round(childScrollableHeight * 68350dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes interpolator.getInterpolation( 68450dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes offsetForView / (float) childScrollableHeight)); 68550dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 68650dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes return Integer.signum(offset) * (child.getTop() + interpolatedDiff); 68750dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes } 688a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 68950dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes 69050dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // If we get to here then the view on the offset isn't suitable for interpolated 69150dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes // scrolling. So break out of the loop 69250dfc33a565c4aefe6d5e844c93aa24a74cb80b3Chris Banes break; 693a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 694a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 695a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 696a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return offset; 697a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 698a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 699a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getTopBottomOffsetForScrollingSibling() { 700a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mSiblingOffsetTop; 701a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 702a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 703a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 704a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 705a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Behavior which should be used by {@link View}s which can scroll vertically and support 706a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * nested scrolling to automatically scroll any {@link AppBarLayout} siblings. 707a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 708a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class ScrollingViewBehavior extends ViewOffsetBehavior<View> { 709a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mOverlayTop; 710a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 711a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior() {} 712a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 713a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior(Context context, AttributeSet attrs) { 714a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 715a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 716a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, 717a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params); 718a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = a.getDimensionPixelSize( 719a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0); 720a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 721a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 722a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 723a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 724a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { 725a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We depend on any AppBarLayouts 726a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return dependency instanceof AppBarLayout; 727a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 728a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 729a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 730a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onMeasureChild(CoordinatorLayout parent, View child, 731a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentWidthMeasureSpec, int widthUsed, 732a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentHeightMeasureSpec, int heightUsed) { 733a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (child.getLayoutParams().height == LayoutParams.MATCH_PARENT) { 734a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the child's height is set to match_parent then it with it's maximum visible 735a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // visible height 736a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 737a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final List<View> dependencies = parent.getDependencies(child); 738a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dependencies.isEmpty()) { 739a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we don't have any dependencies, return false 740a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 741a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 742a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 743a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout appBar = findFirstAppBarLayout(dependencies); 744a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (appBar != null) { 745a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (appBar.getMeasuredWidth() == 0 || appBar.getMeasuredHeight() == 0) { 746a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the AppBar hasn't been measured yet, we need to do it now 747a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes parent.onMeasureChild(appBar, parentWidthMeasureSpec, 748a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes widthUsed, parentHeightMeasureSpec, heightUsed); 749a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 750a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 751a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollRange = appBar.getTotalScrollRange(); 752a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int height = MeasureSpec.getSize(parentHeightMeasureSpec) 753a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes - appBar.getMeasuredHeight() + scrollRange; 754a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 755a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes MeasureSpec.AT_MOST); 756a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 757a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Now measure the scrolling child with the correct height 758a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes parent.onMeasureChild(child, parentWidthMeasureSpec, 759a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes widthUsed, heightMeasureSpec, heightUsed); 760a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 761a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return true; 762a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 763a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 764a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 765a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 766a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 767a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 768a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onDependentViewChanged(CoordinatorLayout parent, View child, 769a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View dependency) { 770a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final CoordinatorLayout.Behavior behavior = 771a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); 772a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (behavior instanceof Behavior) { 773a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Offset the child so that it is below the app-bar (with any overlap) 774a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 775a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int appBarOffset = ((Behavior) behavior) 776a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes .getTopBottomOffsetForScrollingSibling(); 777a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int expandedMax = dependency.getHeight() - mOverlayTop; 778a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int collapsedMin = parent.getHeight() - child.getHeight(); 779a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 780a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mOverlayTop != 0 && dependency instanceof AppBarLayout) { 781a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we have an overlap top, and the dependency is an AppBarLayout, we control 782a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the offset ourselves based on the appbar's scroll progress. This is so that 783a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the scroll happens sequentially rather than linearly 784a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange(); 785a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin, 786a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Math.abs(appBarOffset) / (float) scrollRange)); 787a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 788a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setTopAndBottomOffset(MathUtils.constrain( 789a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes dependency.getHeight() - mOverlayTop + appBarOffset, 790a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes collapsedMin, expandedMax)); 791a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 792a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 793a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 794a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 795a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 796a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 797a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the distance that this view should overlap any {@link AppBarLayout}. 798a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 799a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param overlayTop the distance in px 800a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 801a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop 802a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 803a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOverlayTop(int overlayTop) { 804a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = overlayTop; 805a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 806a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 807a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 808a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the distance that this view should overlap any {@link AppBarLayout}. 809a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 810a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop 811a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 812a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getOverlayTop() { 813a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mOverlayTop; 814a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 815a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 816a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static AppBarLayout findFirstAppBarLayout(List<View> views) { 817a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = views.size(); i < z; i++) { 818a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View view = views.get(i); 819a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (view instanceof AppBarLayout) { 820a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return (AppBarLayout) view; 821a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 822a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 823a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return null; 824a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 825a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 826a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes} 827