diff options
| author | Chris Banes <chrisbanes@google.com> | 2015-08-07 13:05:44 +0100 |
|---|---|---|
| committer | Danny Baumann <dannybaumann@web.de> | 2016-01-13 11:31:13 +0100 |
| commit | 2ae0ed11526a7adc53bc896fea5688e03ef429da (patch) | |
| tree | c81636baa0c10cdbfbd53756952609ccf9880470 | |
| parent | 5757d1ccef4fde8f1e8511e7c820f28c3b297042 (diff) | |
| download | android_frameworks_support-2ae0ed11526a7adc53bc896fea5688e03ef429da.tar.gz android_frameworks_support-2ae0ed11526a7adc53bc896fea5688e03ef429da.tar.bz2 android_frameworks_support-2ae0ed11526a7adc53bc896fea5688e03ef429da.zip | |
AppBarLayout layout/measure fixes
- We no longer display above the scrolling view
over a collapsed AppBarLayout.
- Make sure we measure the scrolling view to fill
any window inset.
- Make sure the ABL is measured before we measure the
the scrolling view.
- Make sure we use the measured height of views when
calculating the scroll ranges.
- Made some pkg-private methods actually private.
BUG: 23025697
Change-Id: I854d97125c26f1584f13ec554e74247cc8c539d6
| -rw-r--r-- | design/src/android/support/design/widget/AppBarLayout.java | 110 |
1 files changed, 70 insertions, 40 deletions
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java index 53e96ba982..78f8f9fb16 100644 --- a/design/src/android/support/design/widget/AppBarLayout.java +++ b/design/src/android/support/design/widget/AppBarLayout.java @@ -194,14 +194,15 @@ public class AppBarLayout extends LinearLayout { } @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + invalidateScrollRanges(); + } + + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); - // Invalidate the scroll ranges - mTotalScrollRange = INVALID_SCROLL_RANGE; - mDownPreScrollRange = INVALID_SCROLL_RANGE; - mDownPreScrollRange = INVALID_SCROLL_RANGE; - mHaveChildWithInterpolator = false; for (int i = 0, z = getChildCount(); i < z; i++) { final View child = getChildAt(i); @@ -215,6 +216,13 @@ public class AppBarLayout extends LinearLayout { } } + private void invalidateScrollRanges() { + // Invalidate the scroll ranges + mTotalScrollRange = INVALID_SCROLL_RANGE; + mDownPreScrollRange = INVALID_SCROLL_RANGE; + mDownScrollRange = INVALID_SCROLL_RANGE; + } + @Override public void setOrientation(int orientation) { if (orientation != VERTICAL) { @@ -283,7 +291,7 @@ public class AppBarLayout extends LinearLayout { return new LayoutParams(p); } - final boolean hasChildWithInterpolator() { + private boolean hasChildWithInterpolator() { return mHaveChildWithInterpolator; } @@ -301,9 +309,7 @@ public class AppBarLayout extends LinearLayout { for (int i = 0, z = getChildCount(); i < z; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - final int childHeight = ViewCompat.isLaidOut(child) - ? child.getHeight() - : child.getMeasuredHeight(); + final int childHeight = child.getMeasuredHeight(); final int flags = lp.mScrollFlags; if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) { @@ -323,25 +329,24 @@ public class AppBarLayout extends LinearLayout { break; } } - final int top = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; - return mTotalScrollRange = (range - top); + return mTotalScrollRange = (range - getTopInset()); } - final boolean hasScrollableChildren() { + private boolean hasScrollableChildren() { return getTotalScrollRange() != 0; } /** * Return the scroll range when scrolling up from a nested pre-scroll. */ - final int getUpNestedPreScrollRange() { + private int getUpNestedPreScrollRange() { return getTotalScrollRange(); } /** * Return the scroll range when scrolling down from a nested pre-scroll. */ - final int getDownNestedPreScrollRange() { + private int getDownNestedPreScrollRange() { if (mDownPreScrollRange != INVALID_SCROLL_RANGE) { // If we already have a valid value, return it return mDownPreScrollRange; @@ -351,9 +356,7 @@ public class AppBarLayout extends LinearLayout { for (int i = getChildCount() - 1; i >= 0; i--) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - final int childHeight = ViewCompat.isLaidOut(child) - ? child.getHeight() - : child.getMeasuredHeight(); + final int childHeight = child.getMeasuredHeight(); final int flags = lp.mScrollFlags; if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) { @@ -379,7 +382,7 @@ public class AppBarLayout extends LinearLayout { /** * Return the scroll range when scrolling down from a nested scroll. */ - final int getDownNestedScrollRange() { + private int getDownNestedScrollRange() { if (mDownScrollRange != INVALID_SCROLL_RANGE) { // If we already have a valid value, return it return mDownScrollRange; @@ -389,9 +392,7 @@ public class AppBarLayout extends LinearLayout { for (int i = 0, z = getChildCount(); i < z; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - int childHeight = ViewCompat.isLaidOut(child) - ? child.getHeight() - : child.getMeasuredHeight(); + int childHeight = child.getMeasuredHeight(); childHeight += lp.topMargin + lp.bottomMargin; final int flags = lp.mScrollFlags; @@ -404,7 +405,7 @@ public class AppBarLayout extends LinearLayout { // For a collapsing exit scroll, we to take the collapsed height into account. // We also return the range straight away since later views can't scroll // beneath us - return range - ViewCompat.getMinimumHeight(child); + return mDownScrollRange = (range - ViewCompat.getMinimumHeight(child)); } } else { // As soon as a view doesn't have the scroll flag, we end the range calculation. @@ -454,14 +455,18 @@ public class AppBarLayout extends LinearLayout { return mTargetElevation; } - int getPendingAction() { + private int getPendingAction() { return mPendingAction; } - void resetPendingAction() { + private void resetPendingAction() { mPendingAction = PENDING_ACTION_NONE; } + private int getTopInset() { + return mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; + } + private void setWindowInsets(WindowInsetsCompat insets) { // Invalidate the total scroll range... mTotalScrollRange = INVALID_SCROLL_RANGE; @@ -1072,6 +1077,10 @@ public class AppBarLayout extends LinearLayout { } } + if (ViewCompat.getFitsSystemWindows(child)) { + childScrollableHeight -= layout.getTopInset(); + } + if (childScrollableHeight > 0) { final int offsetForView = absOffset - child.getTop(); final int interpolatedDiff = Math.round(childScrollableHeight * @@ -1199,7 +1208,7 @@ public class AppBarLayout extends LinearLayout { } @Override - public boolean onMeasureChild(CoordinatorLayout parent, View child, + public boolean onMeasureChild(CoordinatorLayout parent, final View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final int childLpHeight = child.getLayoutParams().height; @@ -1215,7 +1224,19 @@ public class AppBarLayout extends LinearLayout { } final AppBarLayout appBar = findFirstAppBarLayout(dependencies); - if (appBar != null && ViewCompat.isLaidOut(appBar)) { + if (appBar != null && appBar.getVisibility() == View.VISIBLE) { + if (appBar.getMeasuredHeight() <= 0 && appBar.getMeasuredWidth() <= 0) { + // The AppBar hasn't been measured yet, so we'll post a Runnable to delay + // our measure until after the ABLs has happened + child.post(new Runnable() { + @Override + public void run() { + child.requestLayout(); + } + }); + return false; + } + if (ViewCompat.getFitsSystemWindows(appBar)) { // If the AppBarLayout is fitting system windows then we need to also, // otherwise we'll get CoL's compatible layout functionality @@ -1227,6 +1248,7 @@ public class AppBarLayout extends LinearLayout { // If the measure spec doesn't specify a size, use the current height availableHeight = parent.getHeight(); } + final int height = availableHeight - appBar.getMeasuredHeight() + appBar.getTotalScrollRange(); final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, @@ -1251,24 +1273,32 @@ public class AppBarLayout extends LinearLayout { ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); if (behavior instanceof Behavior) { // Offset the child so that it is below the app-bar (with any overlap) + final int offset = ((Behavior) behavior).getTopBottomOffsetForScrollingSibling(); + setTopAndBottomOffset(dependency.getHeight() + offset + - getOverlapForOffset(dependency, offset)); + } + return false; + } - final int appBarOffset = ((Behavior) behavior) - .getTopBottomOffsetForScrollingSibling(); - final int expandedMax = dependency.getHeight() - mOverlayTop; - final int collapsedMin = parent.getHeight() - child.getHeight(); - - if (mOverlayTop != 0 && dependency instanceof AppBarLayout) { - // If we have an overlap top, and the dependency is an AppBarLayout, we control - // the offset ourselves based on the appbar's scroll progress. This is so that - // the scroll happens sequentially rather than linearly - final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange(); - setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin, - Math.abs(appBarOffset) / (float) scrollRange)); + private int getOverlapForOffset(final View dependency, final int offset) { + if (mOverlayTop != 0 && dependency instanceof AppBarLayout) { + final AppBarLayout abl = (AppBarLayout) dependency; + final int totalScrollRange = abl.getTotalScrollRange(); + final int preScrollDown = abl.getDownNestedPreScrollRange(); + if (preScrollDown != 0 && (totalScrollRange + offset) <= preScrollDown) { + // If we're in a pre-scroll down. Don't use the offset at all. + return 0; } else { - setTopAndBottomOffset(dependency.getHeight() - mOverlayTop + appBarOffset); + final int availScrollRange = totalScrollRange - preScrollDown; + if (availScrollRange == 0) { + // Else we'll use a interpolated ratio of the overlap, depending on offset + final float percScrolled = offset / (float) availScrollRange; + return MathUtils.constrain( + Math.round((1f + percScrolled) * mOverlayTop), 0, mOverlayTop); + } } } - return false; + return mOverlayTop; } /** |
