AppBarLayout.java revision a6a508b2296730ca6954aaebcca52a9962a5cb55
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; 21a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.design.R; 22a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat; 23a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.util.AttributeSet; 24a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View; 25a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.ViewGroup; 26a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.animation.Interpolator; 27a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.widget.LinearLayout; 28a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 29a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.lang.ref.WeakReference; 30a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List; 31a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 32a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/** 33a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of 34a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Material Design's App bar concept, namely scrolling gestures. 35a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 36a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Children should provide their desired scrolling behavior through 37a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute: 38a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@code app:layout_scrollFlags}. 39a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 40a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}. 42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will 43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * not work. 44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p> 45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * AppBarLayout also requires a separate scrolling sibling in order to. The binding is done through 46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the {@link ScrollingViewBehavior} beahior class, meaning that you should set your scrolling 47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view's behavior to be an instance of {@link ScrollingViewBehavior}. A string resource containing 48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * the full class name is available. 49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <pre> 51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.CoordinatorLayout 52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:android="http://schemas.android.com/apk/res/android" 53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * xmlns:app="http://schemas.android.com/apk/res-auto" 54a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 55a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent"> 56a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 57a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v4.widget.NestedScrollView 58a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent" 59a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="match_parent" 60a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_behavior="@string/appbar_scrolling_view_behavior"> 61a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 62a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <!-- Your scrolling content --> 63a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 64a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.v4.widget.NestedScrollView> 65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.AppBarLayout 67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_height="wrap_content" 68a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * android:layout_width="match_parent"> 69a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 70a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.v7.widget.Toolbar 71a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 72a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 73a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <android.support.design.widget.TabLayout 75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * ... 76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * app:layout_scrollFlags="scroll|enterAlways"/> 77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.AppBarLayout> 79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </android.support.design.widget.CoordinatorLayout> 81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * </pre> 82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar"> 84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a> 85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class) 87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespublic class AppBarLayout extends LinearLayout { 88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Interface which allows an implementing child {@link View} of this {@link AppBarLayout} to 91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * receive offset updates, and provide extra information. 92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public interface AppBarLayoutChild { 94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows 96a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * child views to implement custom behavior based on the offset (for instance pinning a 97a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * view at a certain y value). 98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 99a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param leftRightOffset the left and right offset, in px 100a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param topBottomOffset the top and bottom offset, in px 101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes void onOffsetUpdate(int leftRightOffset, int topBottomOffset); 103a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 105a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static final int INVALID_SCROLL_RANGE = -1; 106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mTotalScrollRange = INVALID_SCROLL_RANGE; 108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownPreScrollRange = INVALID_SCROLL_RANGE; 109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mDownScrollRange = INVALID_SCROLL_RANGE; 110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes boolean mHaveChildWithInterpolator; 112a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 113a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context) { 114a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes this(context, null); 115a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 116a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 117a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public AppBarLayout(Context context, AttributeSet attrs) { 118a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 119a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setOrientation(VERTICAL); 120a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 121a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 122a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 123a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected void onLayout(boolean changed, int l, int t, int r, int b) { 124a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.onLayout(changed, l, t, r, b); 125a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 126a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Invalidate the scroll ranges 127a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mTotalScrollRange = INVALID_SCROLL_RANGE; 128a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 129a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mDownPreScrollRange = INVALID_SCROLL_RANGE; 130a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 131a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = false; 132a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 133a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 134a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 135a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 136a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 137a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 138a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mHaveChildWithInterpolator = true; 139a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 140a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 141a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 142a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 143a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 144a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 145a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOrientation(int orientation) { 146a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (orientation != VERTICAL) { 147a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes throw new IllegalArgumentException("AppBarLayout is always vertical and does" 148a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes + " not support horizontal orientation"); 149a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 150a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super.setOrientation(orientation); 151a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 152a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 153a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 154a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 155a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return p instanceof LayoutParams; 156a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 157a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 158a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 159a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateDefaultLayoutParams() { 160a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 161a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 162a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 163a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 164a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams generateLayoutParams(AttributeSet attrs) { 165a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(getContext(), attrs); 166a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 167a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 168a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 169a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 170a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (p instanceof LinearLayout.LayoutParams) { 171a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((LinearLayout.LayoutParams) p); 172a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (p instanceof MarginLayoutParams) { 173a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams((MarginLayoutParams) p); 174a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 175a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return new LayoutParams(p); 176a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 177a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 178a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasChildWithInterpolator() { 179a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mHaveChildWithInterpolator; 180a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 181a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 182a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 183a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range of any children 184a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 185a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @return the scroll range in px 186a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 187a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getTotalScrollRange() { 188a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mTotalScrollRange != INVALID_SCROLL_RANGE) { 189a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange; 190a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 191a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 192a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 193a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = getChildCount(); i < z; i++) { 194a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 195a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 196a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 197a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 198a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 199a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 200a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 201a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 202a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 203a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 204a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 205a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 206a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 207a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We also return the range straight away since later views can't scroll beneath 208a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us 209a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return range - ViewCompat.getMinimumHeight(child); 210a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 211a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 212a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 213a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 214a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 215a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 216a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 217a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mTotalScrollRange = range; 218a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 219a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 220a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final boolean hasScrollableChildren() { 221a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return getTotalScrollRange() != 0; 222a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 223a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 224a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 225a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling up from a nested pre-scroll. 226a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 227a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getUpNestedPreScrollRange() { 228a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return getTotalScrollRange() - getUpNestedScrollRange(); 229a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 230a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 231a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 232a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling up from a nested scroll. 233a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @return 234a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 235a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getUpNestedScrollRange() { 236a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return 0; 237a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 238a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 239a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 240a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested pre-scroll. 241a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 242a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedPreScrollRange() { 243a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownPreScrollRange != INVALID_SCROLL_RANGE) { 244a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 245a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange; 246a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 247a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 248a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 249a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 250a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 251a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 252a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 253a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 254a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 255a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 256a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 257a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) { 258a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // The view has the quick return flag combination... 259a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) { 260a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If they're set to enter collapsed, use the minimum height 261a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += ViewCompat.getMinimumHeight(child); 262a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 263a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Else use the full height 264a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 265a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 266a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (range > 0) { 267a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we've hit an non-quick return scrollable view, and we've already hit a 268a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // quick return view, return now 269a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 270a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 271a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 272a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownPreScrollRange = range; 273a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 274a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 275a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getMinimumHeightForVisibleOverlappingContent() { 276a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int minHeight = ViewCompat.getMinimumHeight(this); 277a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (minHeight != 0) { 278a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If this layout has a min height, use it (doubled) 279a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return minHeight * 2; 280a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 281a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 282a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Otherwise, we'll use twice the min height of our last child 283a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childCount = getChildCount(); 284a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return childCount >= 1 285a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2 286a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : 0; 287a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 288a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 289a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 290a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Return the scroll range when scrolling down from a nested scroll. 291a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 292a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getDownNestedScrollRange() { 293a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mDownScrollRange != INVALID_SCROLL_RANGE) { 294a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we already have a valid value, return it 295a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange; 296a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 297a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 298a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int range = 0; 299a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = getChildCount() - 1; i >= 0; i--) { 300a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = getChildAt(i); 301a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 302a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = ViewCompat.isLaidOut(child) 303a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? child.getHeight() 304a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : child.getMeasuredHeight(); 305a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 306a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int flags = lp.mScrollFlags; 307a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 308a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 310a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes range += childHeight; 311a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 312a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 313a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing exit scroll, we to take the collapsed height into account. 314a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We also return the range straight away since later views can't scroll 315a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // beneath us 316a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return range - ViewCompat.getMinimumHeight(child); 317a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 318a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 319a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // As soon as a view doesn't have the scroll flag, we end the range calculation. 320a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // This is because views below can not scroll under a fixed view. 321a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 322a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 323a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 324a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mDownScrollRange = range; 325a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 326a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 327a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class LayoutParams extends LinearLayout.LayoutParams { 328a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The view will be scroll in direct relation to scroll events. This flag needs to be 330a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * set for any of the other flags to take effect. If any sibling views 331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * before this one do not have this flag, then this value has no effect. 332a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 333a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_SCROLL = 0x1; 334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 336a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When exiting (scrolling off screen) the view will be scrolled until it is 337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 'collapsed'. The collapsed height is defined by the view's minimum height. 338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 339a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2; 343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * When entering (scrolling on screen) the view will scroll on any downwards 346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll event, regardless of whether the scrolling view is also scrolling. This 347a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * is commonly referred to as the 'quick return' pattern. 348a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 349a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4; 350a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 351a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 352a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * An additional flag for 'enterAlways' which modifies the returning view to 353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * only initially scroll back to it's collapsed height. Once the scrolling view has 354a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * reached the end of it's scroll range, the remainder of this view will be scrolled 355a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * into view. The collapsed height is defined by the view's minimum height. 356a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 357a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see ViewCompat#getMinimumHeight(View) 358a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see View#setMinimumHeight(int) 359a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 360a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8; 361a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 362a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Internal flag which allows quick checking of 'quick return' 364a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 365a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS; 366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 367a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int mScrollFlags = SCROLL_FLAG_SCROLL; 368a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Interpolator mScrollInterpolator; 369a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 370a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(Context c, AttributeSet attrs) { 371a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(c, attrs); 372a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_LayoutParams); 373a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = a.getInt(R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags, 0); 374a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (a.hasValue(R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator)) { 375a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int resId = a.getResourceId( 376a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator, 0); 377a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator( 378a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes c, resId); 379a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 380a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height) { 384a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height); 385a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 386a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 387a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(int width, int height, float weight) { 388a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(width, height, weight); 389a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 390a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 391a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(ViewGroup.LayoutParams p) { 392a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(p); 393a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 394a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 395a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(MarginLayoutParams source) { 396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 397a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 398a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 399a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LinearLayout.LayoutParams source) { 400a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 401a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 402a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public LayoutParams(LayoutParams source) { 404a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(source); 405a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = source.mScrollFlags; 406a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = source.mScrollInterpolator; 407a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the scrolling flags. 411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL}, 413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS} 414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * and {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED}. 415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollFlags() 417a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags 419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 420a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setScrollFlags(int flags) { 421a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollFlags = flags; 422a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 423a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 424a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 425a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the scrolling flags. 426a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 427a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollFlags(int) 428a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 429a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags 430a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 431a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getScrollFlags() { 432a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollFlags; 433a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 434a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 435a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 436a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the interpolator to when scrolling the view associated with this 437a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. 438a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 439a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling. 440a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 441a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator 442a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #getScrollInterpolator() 443a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 444a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setScrollInterpolator(Interpolator interpolator) { 445a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScrollInterpolator = interpolator; 446a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 447a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 448a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 449a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the {@link Interpolator} being used for scrolling the view associated with this 450a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling. 451a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 452a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollInterpolator 453a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @see #setScrollInterpolator(Interpolator) 454a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 455a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Interpolator getScrollInterpolator() { 456a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mScrollInterpolator; 457a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 458a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 459a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 460a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 461a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested 462a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * scroll handling with offsetting. 463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 464a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class Behavior extends ViewOffsetBehavior<AppBarLayout> { 465a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mSiblingOffsetTop; 466a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 467a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior() {} 468a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 469a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public Behavior(Context context, AttributeSet attrs) { 470a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 471a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 472a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 473a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 474a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 475a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View directTargetChild, View target, int nestedScrollAxes) { 476a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Return true if we're nested scrolling vertically and we have scrollable children 477a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 478a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes && child.hasScrollableChildren(); 479a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 480a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 481a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 482a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 483a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dx, int dy, int[] consumed) { 484a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dy != 0) { 485a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes consumed[1] = scroll(coordinatorLayout, child, dy, dy < 0 486a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? -child.getDownNestedPreScrollRange() 487a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : -child.getUpNestedPreScrollRange()); 488a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 489a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 490a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 491a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 492a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 493a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View target, int dxConsumed, int dyConsumed, 494a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int dxUnconsumed, int dyUnconsumed) { 495a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int dy = 0; 496a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dyConsumed > 0) { 497a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the scrolling view is scrolling up, we offset 498a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes dy = dyConsumed; 499a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (dyUnconsumed < 0) { 500a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the scrolling view is scrolling down, we offset anything it doesn't consume 501a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes dy = dyUnconsumed; 502a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 503a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 504a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dy != 0) { 505a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes scroll(coordinatorLayout, child, dy, dy < 0 506a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? -child.getDownNestedScrollRange() 507a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : -child.getUpNestedScrollRange()); 508a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 509a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 510a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 511a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout, 512a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int dy, int minOffset) { 513a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout, 514a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mSiblingOffsetTop - dy, 515a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes minOffset); 516a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 517a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout, 519a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes AppBarLayout appBarLayout, int newOffset, final int minOffset) { 520a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int curOffset = mSiblingOffsetTop; 521a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int consumed = 0; 522a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 523a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (curOffset >= minOffset) { 524a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes newOffset = MathUtils.constrain(newOffset, minOffset, 0); 525a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 526a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (curOffset != newOffset) { 527a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes boolean offsetChanged = setTopAndBottomOffset( 528a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes appBarLayout.hasChildWithInterpolator() 529a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ? interpolateOffset(appBarLayout, newOffset) 530a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes : newOffset); 531a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update how much dy we have consumed 532a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes consumed = curOffset - newOffset; 533a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Update the stored sibling offset 534a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mSiblingOffsetTop = newOffset; 535a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 536a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) { 537a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the offset hasn't changed and we're using an interpolated scroll 538a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // then we need to keep any dependent views updated. CoL will do this for 539a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // us when we move, but we need to do it manually when we don't (as an 540a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // interpolated scroll may finish early). 541a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes coordinatorLayout.dispatchDependentViewsChanged(appBarLayout); 542a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 543a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 544a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes dispatchOffsetUpdates(appBarLayout); 545a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 546a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 547a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 548a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return consumed; 549a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 550a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 551a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private void dispatchOffsetUpdates(AppBarLayout layout) { 552a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 553a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View child = layout.getChildAt(i); 554a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (child instanceof AppBarLayoutChild) { 555a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ((AppBarLayoutChild) child).onOffsetUpdate( 556a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes getLeftAndRightOffset(), getTopAndBottomOffset()); 557a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 558a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 559a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 560a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 561a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int interpolateOffset(AppBarLayout layout, final int offset) { 562a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int heightSoFar = 0; 563a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int absOffset = Math.abs(offset); 564a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 565a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = layout.getChildCount(); i < z; i++) { 566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View child = layout.getChildAt(i); 567a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams(); 568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollFlags = childLp.getScrollFlags(); 569a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int previousHeightSoFar = heightSoFar; 570a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int childHeight = child.getHeight(); 571a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 572a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int childScrollableHeight = 0; 573a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((scrollFlags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { 574a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're set to scroll so add the child's height 575a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes childScrollableHeight += child.getHeight(); 576a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if ((scrollFlags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) { 577a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // For a collapsing scroll, we to take the collapsed height into account. 578a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes childScrollableHeight -= ViewCompat.getMinimumHeight(child); 579a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 580a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 581a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 582a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Now update the height encountered 583a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes heightSoFar += childHeight; 584a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 585a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (absOffset >= previousHeightSoFar && absOffset <= heightSoFar) { 586a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final Interpolator interpolator = childLp.getScrollInterpolator(); 587a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 588a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (interpolator != null) { 589a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int offsetForView = absOffset - previousHeightSoFar; 590a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int interpolatedDiff = Math.round(childScrollableHeight * 591a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes interpolator.getInterpolation( 592a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes offsetForView / (float) childScrollableHeight)); 593a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 594a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return Integer.signum(offset) * (previousHeightSoFar + interpolatedDiff); 595a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 596a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes break; 597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 600a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 601a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return offset; 602a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 603a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 604a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int getTopBottomOffsetForScrollingSibling() { 605a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mSiblingOffsetTop; 606a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 607a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 608a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 609a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 610a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Behavior which should be used by {@link View}s which can scroll vertically and support 611a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * nested scrolling to automatically scroll any {@link AppBarLayout} siblings. 612a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 613a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public static class ScrollingViewBehavior extends ViewOffsetBehavior<View> { 614a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private int mOverlayTop; 615a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 616a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior() {} 617a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 618a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public ScrollingViewBehavior(Context context, AttributeSet attrs) { 619a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes super(context, attrs); 620a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 621a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, 622a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params); 623a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = a.getDimensionPixelSize( 624a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes R.styleable.ScrollingViewBehavior_Params_behavior_overlapTop, 0); 625a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes a.recycle(); 626a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 627a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 628a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 629a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { 630a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We depend on any AppBarLayouts 631a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return dependency instanceof AppBarLayout; 632a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 633a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 634a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 635a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onMeasureChild(CoordinatorLayout parent, View child, 636a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentWidthMeasureSpec, int widthUsed, 637a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes int parentHeightMeasureSpec, int heightUsed) { 638a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (child.getLayoutParams().height == LayoutParams.MATCH_PARENT) { 639a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the child's height is set to match_parent then it with it's maximum visible 640a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // visible height 641a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 642a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final List<View> dependencies = parent.getDependencies(child); 643a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dependencies.isEmpty()) { 644a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we don't have any dependencies, return false 645a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 646a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 647a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 648a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final AppBarLayout appBar = findFirstAppBarLayout(dependencies); 649a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (appBar != null) { 650a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (appBar.getMeasuredWidth() == 0 || appBar.getMeasuredHeight() == 0) { 651a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If the AppBar hasn't been measured yet, we need to do it now 652a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes parent.onMeasureChild(appBar, parentWidthMeasureSpec, 653a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes widthUsed, parentHeightMeasureSpec, heightUsed); 654a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 655a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 656a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollRange = appBar.getTotalScrollRange(); 657a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int height = MeasureSpec.getSize(parentHeightMeasureSpec) 658a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes - appBar.getMeasuredHeight() + scrollRange; 659a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 660a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes MeasureSpec.AT_MOST); 661a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 662a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Now measure the scrolling child with the correct height 663a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes parent.onMeasureChild(child, parentWidthMeasureSpec, 664a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes widthUsed, heightMeasureSpec, heightUsed); 665a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 666a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return true; 667a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 668a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 669a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 670a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 671a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 672a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes @Override 673a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public boolean onDependentViewChanged(CoordinatorLayout parent, View child, 674a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View dependency) { 675a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final CoordinatorLayout.Behavior behavior = 676a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); 677a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (behavior instanceof Behavior) { 678a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // Offset the child so that it is below the app-bar (with any overlap) 679a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 680a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int appBarOffset = ((Behavior) behavior) 681a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes .getTopBottomOffsetForScrollingSibling(); 682a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int expandedMax = dependency.getHeight() - mOverlayTop; 683a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int collapsedMin = parent.getHeight() - child.getHeight(); 684a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 685a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mOverlayTop != 0 && dependency instanceof AppBarLayout) { 686a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // If we have an overlap top, and the dependency is an AppBarLayout, we control 687a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the offset ourselves based on the appbar's scroll progress. This is so that 688a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // the scroll happens sequentially rather than linearly 689a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange(); 690a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin, 691a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes Math.abs(appBarOffset) / (float) scrollRange)); 692a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else { 693a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes setTopAndBottomOffset(MathUtils.constrain( 694a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes dependency.getHeight() - mOverlayTop + appBarOffset, 695a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes collapsedMin, expandedMax)); 696a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 697a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 698a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return false; 699a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 700a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 701a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 702a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Set the distance that this view should overlap any {@link AppBarLayout}. 703a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 704a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @param overlayTop the distance in px 705a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 706a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop 707a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 708a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public void setOverlayTop(int overlayTop) { 709a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mOverlayTop = overlayTop; 710a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 711a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 712a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes /** 713a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Returns the distance that this view should overlap any {@link AppBarLayout}. 714a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * 715a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * @attr ref android.support.design.R.styleable.ScrollingViewBehavior_LayoutParams_layout_overlapTop 716a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */ 717a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes public int getOverlayTop() { 718a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return mOverlayTop; 719a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 720a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 721a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private static AppBarLayout findFirstAppBarLayout(List<View> views) { 722a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = views.size(); i < z; i++) { 723a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View view = views.get(i); 724a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (view instanceof AppBarLayout) { 725a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return (AppBarLayout) view; 726a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 727a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 728a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return null; 729a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 730a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 731a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes} 732