am 61c2d94f: Merge "AppBarLayout improvements" into lmp-mr1-ub-dev
* commit '61c2d94faeb79a7a96df402f201a226ed740694a':
AppBarLayout improvements
diff --git a/design/api/current.txt b/design/api/current.txt
index 87fb679..8dd4fd8 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -6,15 +6,19 @@
}
public static abstract interface AppBarLayout.AppBarLayoutChild {
- method public abstract void onOffsetUpdate(int, int);
+ method public abstract int onOffsetUpdate(int);
+ field public static final int STATE_ELEVATED_ABOVE = 1; // 0x1
+ field public static final int STATE_ELEVATED_INLINE = 0; // 0x0
}
public static class AppBarLayout.Behavior extends android.support.design.widget.ViewOffsetBehavior {
ctor public AppBarLayout.Behavior();
ctor public AppBarLayout.Behavior(android.content.Context, android.util.AttributeSet);
+ method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, int);
method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int[]);
method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int, int);
method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, android.view.View, int);
+ method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View);
}
public static class AppBarLayout.LayoutParams extends android.widget.LinearLayout.LayoutParams {
@@ -50,7 +54,7 @@
ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet);
ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet, int);
method public int getForegroundScrimColor();
- method public void onOffsetUpdate(int, int);
+ method public int onOffsetUpdate(int);
method public void setCollapsedTitleTextAppearance(int);
method public void setCollapsedTitleTextColor(int);
method public void setExpandedTitleColor(int);
@@ -67,6 +71,8 @@
ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
ctor public CollapsingToolbarLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
ctor public CollapsingToolbarLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+ method public int getCollapseMode();
+ method public float getParallaxMultiplier();
method public void setCollapseMode(int);
method public void setParallaxMultiplier(float);
field public static final int COLLAPSE_MODE_OFF = 0; // 0x0
diff --git a/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java b/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java
new file mode 100644
index 0000000..f080592
--- /dev/null
+++ b/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.widget;
+
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+class ViewUtilsLollipop {
+
+ static void setBoundsViewOutlineProvider(View view) {
+ view.setOutlineProvider(ViewOutlineProvider.BOUNDS);
+ }
+
+}
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index c018923..87ed281 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -160,6 +160,10 @@
<attr name="maxActionInlineWidth" format="dimension"/>
</declare-styleable>
+ <declare-styleable name="AppBarLayout">
+ <attr name="elevation" />
+ </declare-styleable>
+
<declare-styleable name="AppBarLayout_LayoutParams">
<attr name="layout_scrollFlags">
<!-- The view will be scroll in direct relation to scroll events. This flag needs to be
diff --git a/design/res/values/dimens.xml b/design/res/values/dimens.xml
index f3a9111..3c8f77e 100644
--- a/design/res/values/dimens.xml
+++ b/design/res/values/dimens.xml
@@ -50,4 +50,6 @@
<dimen name="snackbar_text_size">14sp</dimen>
+ <dimen name="appbar_elevation">4dp</dimen>
+
</resources>
diff --git a/design/res/values/styles.xml b/design/res/values/styles.xml
index 572e278..f77c78e 100644
--- a/design/res/values/styles.xml
+++ b/design/res/values/styles.xml
@@ -100,5 +100,9 @@
<item name="foregroundScrimColor">@android:color/transparent</item>
</style>
+ <style name="Widget.Design.AppBarLayout" parent="android:Widget">
+ <item name="elevation">@dimen/appbar_elevation</item>
+ </style>
+
</resources>
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java
index dc291ef..bf55293 100644
--- a/design/src/android/support/design/widget/AppBarLayout.java
+++ b/design/src/android/support/design/widget/AppBarLayout.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.TypedArray;
+import android.support.annotation.IntDef;
import android.support.design.R;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
@@ -26,7 +27,8 @@
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
-import java.lang.ref.WeakReference;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -91,15 +93,44 @@
* receive offset updates, and provide extra information.
*/
public interface AppBarLayoutChild {
+
+ /** @hide */
+ @IntDef({
+ STATE_ELEVATED_ABOVE,
+ STATE_ELEVATED_INLINE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ElevatedState {}
+
+ /**
+ * The {@link AppBarLayout} should be elevated above any scrolling content, and this cast
+ * a shadow.
+ *
+ * @see #onOffsetUpdate(int)
+ */
+ int STATE_ELEVATED_ABOVE = 1;
+
+ /**
+ * The {@link AppBarLayout} should not be elevated above any scrolling content.
+ *
+ * @see #onOffsetUpdate(int)
+ */
+ int STATE_ELEVATED_INLINE = 0;
+
/**
* Called when the {@link AppBarLayout}'s layout offset has been changed. This allows
* child views to implement custom behavior based on the offset (for instance pinning a
* view at a certain y value).
*
- * @param leftRightOffset the left and right offset, in px
- * @param topBottomOffset the top and bottom offset, in px
+ * <p>You can influence the elevation of the {@link AppBarLayout} by returning one of
+ * {@link #STATE_ELEVATED_INLINE} or {@link #STATE_ELEVATED_ABOVE}.
+ *
+ * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px
+ *
+ * @return one of {@link #STATE_ELEVATED_INLINE} or {@link #STATE_ELEVATED_ABOVE}.
*/
- void onOffsetUpdate(int leftRightOffset, int topBottomOffset);
+ @ElevatedState
+ int onOffsetUpdate(int verticalOffset);
}
private static final int INVALID_SCROLL_RANGE = -1;
@@ -110,6 +141,8 @@
boolean mHaveChildWithInterpolator;
+ private float mTargetElevation;
+
public AppBarLayout(Context context) {
this(context, null);
}
@@ -117,6 +150,14 @@
public AppBarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(VERTICAL);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout,
+ 0, R.style.Widget_Design_AppBarLayout);
+ mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0);
+ a.recycle();
+
+ // Use the bounds view outline provider so that we cast a shadow, even without a background
+ ViewUtils.setBoundsViewOutlineProvider(this);
}
@Override
@@ -225,15 +266,7 @@
* Return the scroll range when scrolling up from a nested pre-scroll.
*/
final int getUpNestedPreScrollRange() {
- return getTotalScrollRange() - getUpNestedScrollRange();
- }
-
- /**
- * Return the scroll range when scrolling up from a nested scroll.
- * @return
- */
- final int getUpNestedScrollRange() {
- return 0;
+ return getTotalScrollRange();
}
/**
@@ -272,20 +305,6 @@
return mDownPreScrollRange = range;
}
- final int getMinimumHeightForVisibleOverlappingContent() {
- final int minHeight = ViewCompat.getMinimumHeight(this);
- if (minHeight != 0) {
- // If this layout has a min height, use it (doubled)
- return minHeight * 2;
- }
-
- // Otherwise, we'll use twice the min height of our last child
- final int childCount = getChildCount();
- return childCount >= 1
- ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2
- : 0;
- }
-
/**
* Return the scroll range when scrolling down from a nested scroll.
*/
@@ -324,7 +343,39 @@
return mDownScrollRange = range;
}
+ final int getMinimumHeightForVisibleOverlappingContent() {
+ final int minHeight = ViewCompat.getMinimumHeight(this);
+ if (minHeight != 0) {
+ // If this layout has a min height, use it (doubled)
+ return minHeight * 2;
+ }
+
+ // Otherwise, we'll use twice the min height of our last child
+ final int childCount = getChildCount();
+ return childCount >= 1
+ ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) * 2
+ : 0;
+ }
+
+ /**
+ * The elevation value to use when {@link AppBarLayout} is elevated above content.
+ */
+ final float getTargetElevation() {
+ return mTargetElevation;
+ }
+
public static class LayoutParams extends LinearLayout.LayoutParams {
+
+ /** @hide */
+ @IntDef(flag=true, value={
+ SCROLL_FLAG_SCROLL,
+ SCROLL_FLAG_EXIT_UNTIL_COLLAPSED,
+ SCROLL_FLAG_ENTER_ALWAYS,
+ SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ScrollFlags {}
+
/**
* The view will be scroll in direct relation to scroll events. This flag needs to be
* set for any of the other flags to take effect. If any sibling views
@@ -417,7 +468,7 @@
*
* @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags
*/
- public void setScrollFlags(int flags) {
+ public void setScrollFlags(@ScrollFlags int flags) {
mScrollFlags = flags;
}
@@ -428,6 +479,7 @@
*
* @attr ref android.support.design.R.styleable.AppBarLayout_LayoutParams_layout_scrollFlags
*/
+ @ScrollFlags
public int getScrollFlags() {
return mScrollFlags;
}
@@ -464,6 +516,8 @@
public static class Behavior extends ViewOffsetBehavior<AppBarLayout> {
private int mSiblingOffsetTop;
+ private boolean mSkipNestedPreScroll;
+
public Behavior() {}
public Behavior(Context context, AttributeSet attrs) {
@@ -481,10 +535,18 @@
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View target, int dx, int dy, int[] consumed) {
- if (dy != 0) {
- consumed[1] = scroll(coordinatorLayout, child, dy, dy < 0
- ? -child.getDownNestedPreScrollRange()
- : -child.getUpNestedPreScrollRange());
+ if (dy != 0 && !mSkipNestedPreScroll) {
+ int min, max;
+ if (dy < 0) {
+ // We're scrolling down
+ min = -child.getTotalScrollRange();
+ max = min + child.getDownNestedPreScrollRange();
+ } else {
+ // We're scrolling up
+ min = -child.getUpNestedPreScrollRange();
+ max = 0;
+ }
+ consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
}
}
@@ -492,36 +554,51 @@
public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed) {
- int dy = 0;
- if (dyConsumed > 0) {
- // If the scrolling view is scrolling up, we offset
- dy = dyConsumed;
- } else if (dyUnconsumed < 0) {
- // If the scrolling view is scrolling down, we offset anything it doesn't consume
- dy = dyUnconsumed;
+ if (dyUnconsumed < 0) {
+ // If the scrolling view is scrolling down but not consuming, it's probably be at
+ // the top of it's content
+ scroll(coordinatorLayout, child, dyUnconsumed,
+ -child.getDownNestedScrollRange(), 0);
+ // Set the expanding flag so that onNestedPreScroll doesn't handle any events
+ mSkipNestedPreScroll = true;
+ } else {
+ // As we're no longer handling nested scrolls, reset the skip flag
+ mSkipNestedPreScroll = false;
}
+ }
- if (dy != 0) {
- scroll(coordinatorLayout, child, dy, dy < 0
- ? -child.getDownNestedScrollRange()
- : -child.getUpNestedScrollRange());
- }
+ @Override
+ public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
+ View target) {
+ // Reset the skip flag
+ mSkipNestedPreScroll = false;
+ }
+
+ @Override
+ public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout child,
+ int layoutDirection) {
+ boolean handled = super.onLayoutChild(parent, child, layoutDirection);
+
+ // Make sure we update the elevation
+ final int elevationState = dispatchOffsetUpdates(child);
+ checkElevation(child, getTopAndBottomOffset(), elevationState);
+
+ return handled;
}
private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout,
- int dy, int minOffset) {
+ int dy, int minOffset, int maxOffset) {
return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout,
- mSiblingOffsetTop - dy,
- minOffset);
+ mSiblingOffsetTop - dy, minOffset, maxOffset);
}
private int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout,
- AppBarLayout appBarLayout, int newOffset, final int minOffset) {
+ AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) {
final int curOffset = mSiblingOffsetTop;
int consumed = 0;
- if (curOffset >= minOffset) {
- newOffset = MathUtils.constrain(newOffset, minOffset, 0);
+ if (minOffset != 0) {
+ newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset);
if (curOffset != newOffset) {
boolean offsetChanged = setTopAndBottomOffset(
@@ -541,60 +618,78 @@
coordinatorLayout.dispatchDependentViewsChanged(appBarLayout);
}
- dispatchOffsetUpdates(appBarLayout);
+ // Dispatch the updates to any AppBarLayoutChild children
+ final int childState = dispatchOffsetUpdates(appBarLayout);
+ checkElevation(appBarLayout, newOffset, childState);
}
}
return consumed;
}
- private void dispatchOffsetUpdates(AppBarLayout layout) {
- for (int i = 0, z = layout.getChildCount(); i < z; i++) {
- View child = layout.getChildAt(i);
- if (child instanceof AppBarLayoutChild) {
- ((AppBarLayoutChild) child).onOffsetUpdate(
- getLeftAndRightOffset(), getTopAndBottomOffset());
+ private void checkElevation(AppBarLayout appBarLayout, int offset, int childState) {
+ if (appBarLayout.getHeight() + offset == 0) {
+ // If we're not visible, clear out the elevation
+ ViewCompat.setElevation(appBarLayout, 0f);
+ } else {
+ if (childState == AppBarLayoutChild.STATE_ELEVATED_ABOVE) {
+ ViewCompat.setElevation(appBarLayout, appBarLayout.getTargetElevation());
+ } else {
+ ViewCompat.setElevation(appBarLayout, 0f);
}
}
}
+ private int dispatchOffsetUpdates(AppBarLayout layout) {
+ for (int i = 0, z = layout.getChildCount(); i < z; i++) {
+ View child = layout.getChildAt(i);
+ if (child instanceof AppBarLayoutChild) {
+ final int childState = ((AppBarLayoutChild) child)
+ .onOffsetUpdate(getTopAndBottomOffset());
+
+ if (childState == AppBarLayoutChild.STATE_ELEVATED_INLINE) {
+ return childState;
+ }
+ }
+ }
+
+ return AppBarLayoutChild.STATE_ELEVATED_ABOVE;
+ }
+
private int interpolateOffset(AppBarLayout layout, final int offset) {
- int heightSoFar = 0;
final int absOffset = Math.abs(offset);
for (int i = 0, z = layout.getChildCount(); i < z; i++) {
final View child = layout.getChildAt(i);
final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
- final int scrollFlags = childLp.getScrollFlags();
- final int previousHeightSoFar = heightSoFar;
- final int childHeight = child.getHeight();
+ final Interpolator interpolator = childLp.getScrollInterpolator();
- int childScrollableHeight = 0;
- if ((scrollFlags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
- // We're set to scroll so add the child's height
- childScrollableHeight += child.getHeight();
- if ((scrollFlags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
- // For a collapsing scroll, we to take the collapsed height into account.
- childScrollableHeight -= ViewCompat.getMinimumHeight(child);
- }
- }
-
- // Now update the height encountered
- heightSoFar += childHeight;
-
- if (absOffset >= previousHeightSoFar && absOffset <= heightSoFar) {
- final Interpolator interpolator = childLp.getScrollInterpolator();
-
+ if (absOffset >= child.getTop() && absOffset <= child.getBottom()) {
if (interpolator != null) {
- final int offsetForView = absOffset - previousHeightSoFar;
- final int interpolatedDiff = Math.round(childScrollableHeight *
- interpolator.getInterpolation(
- offsetForView / (float) childScrollableHeight));
+ int childScrollableHeight = 0;
+ final int flags = childLp.getScrollFlags();
+ if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
+ // We're set to scroll so add the child's height
+ childScrollableHeight += child.getHeight();
+ if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
+ // For a collapsing scroll, we to take the collapsed height into account.
+ childScrollableHeight -= ViewCompat.getMinimumHeight(child);
+ }
+ }
- return Integer.signum(offset) * (previousHeightSoFar + interpolatedDiff);
- } else {
- break;
+ if (childScrollableHeight > 0) {
+ final int offsetForView = absOffset - child.getTop();
+ final int interpolatedDiff = Math.round(childScrollableHeight *
+ interpolator.getInterpolation(
+ offsetForView / (float) childScrollableHeight));
+
+ return Integer.signum(offset) * (child.getTop() + interpolatedDiff);
+ }
}
+
+ // If we get to here then the view on the offset isn't suitable for interpolated
+ // scrolling. So break out of the loop
+ break;
}
}
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index 418a2ad..ab36ed5 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -22,6 +22,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.support.annotation.ColorRes;
+import android.support.annotation.IntDef;
import android.support.design.R;
import android.support.v4.graphics.ColorUtils;
import android.support.v4.view.ViewCompat;
@@ -34,6 +35,9 @@
import android.view.animation.Transformation;
import android.widget.FrameLayout;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* CollapsingToolbarLayout is a wrapper for {@link Toolbar} which implements a collapsing app bar.
* It is designed to be used as a direct child of a {@link AppBarLayout}.
@@ -45,7 +49,7 @@
* {@link #setTitle(CharSequence)}. The title appearance can be tweaked via the
* {@code collapsedTextAppearance} and {@code expandedTextAppearance} attributes.
*
- * <h3>Background scrim</h3>
+ * <h3>Foreground scrim</h3>
* A full-bleed scrim which is show or hidden when the scroll position has hit a certain threshold.
* You can change the color via {@link #setForegroundScrimColor(int)}.
*
@@ -69,7 +73,7 @@
*/
public class CollapsingToolbarLayout extends FrameLayout implements AppBarLayout.AppBarLayoutChild {
- private static final int SCRIM_ANIMATION_DURATION = 200;
+ private static final int SCRIM_ANIMATION_DURATION = 600;
private Toolbar mToolbar;
private View mDummyView;
@@ -165,6 +169,12 @@
public void draw(Canvas canvas) {
super.draw(canvas);
+ // If we don't have a toolbar, the scrim will be not be drawn in drawChild() below.
+ // Instead, we draw it here, before our collapsing text.
+ if (mToolbar == null && Color.alpha(mCurrentForegroundColor) > 0) {
+ canvas.drawColor(mCurrentForegroundColor);
+ }
+
// Let the collapsing text helper draw it's text
mCollapsingTextHelper.draw(canvas);
}
@@ -177,6 +187,7 @@
if (child == mToolbar && Color.alpha(mCurrentForegroundColor) > 0) {
canvas.drawColor(mCurrentForegroundColor);
}
+
// Carry on drawing the child...
return super.drawChild(canvas, child, drawingTime);
}
@@ -227,57 +238,73 @@
* @hide
*/
@Override
- public void onOffsetUpdate(int leftRightOffset, int topBottomOffset) {
- boolean toolbarOffsetChanged = false;
+ public int onOffsetUpdate(int verticalOffset) {
+ int pinnedHeight = 0;
for (int i = 0, z = getChildCount(); i < z; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final ViewOffsetHelper offsetHelper = getViewOffsetHelper(child);
- boolean offsetChanged = false;
switch (lp.mCollapseMode) {
case LayoutParams.COLLAPSE_MODE_PIN:
- if (getHeight() + topBottomOffset >= child.getHeight()) {
- offsetChanged = offsetHelper.setTopAndBottomOffset(-topBottomOffset);
+ if (getHeight() + verticalOffset >= child.getHeight()) {
+ offsetHelper.setTopAndBottomOffset(-verticalOffset);
}
+ pinnedHeight += child.getHeight();
break;
case LayoutParams.COLLAPSE_MODE_PARALLAX:
- offsetChanged = offsetHelper.setTopAndBottomOffset(
- Math.round(-topBottomOffset * lp.mParallaxMult));
+ offsetHelper.setTopAndBottomOffset(
+ Math.round(-verticalOffset * lp.mParallaxMult));
break;
}
+ }
- toolbarOffsetChanged = child == mToolbar && offsetChanged;
-
- // Show or hide the scrim if needed
- if (Color.alpha(mForegroundScrimColor) > 0) {
- if (Math.abs(topBottomOffset) < getScrimTriggerOffset()) {
- hideScrim();
- } else {
- showScrim();
- }
+ // Show or hide the scrim if needed
+ if (Color.alpha(mForegroundScrimColor) > 0) {
+ if (getHeight() + verticalOffset < getScrimTriggerOffset()) {
+ showScrim();
+ } else {
+ hideScrim();
}
}
// Update the collapsing text's fraction
- mCollapsingTextHelper.setExpansionFraction(Math.abs(topBottomOffset) /
+ mCollapsingTextHelper.setExpansionFraction(Math.abs(verticalOffset) /
(float) (getHeight() - ViewCompat.getMinimumHeight(this)));
+
+ if (pinnedHeight > 0 && (getHeight() + verticalOffset) == pinnedHeight) {
+ // If we have some pinned children, and we're offset to only show those views,
+ // we want to be elevate
+ return STATE_ELEVATED_ABOVE;
+ } else {
+ // Otherwise, we're inline with the content
+ return STATE_ELEVATED_INLINE;
+ }
}
private void showScrim() {
if (mScrimIsShown) return;
+
Animation anim = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final int originalAlpha = Color.alpha(mForegroundScrimColor);
mCurrentForegroundColor = ColorUtils.setAlphaComponent(mForegroundScrimColor,
AnimationUtils.lerp(0, originalAlpha, interpolatedTime));
+
+ // We need to manually invalidate ourselves and the Toolbar to ensure the scrim
+ // is drawn
+ invalidate();
+ if (mToolbar != null) {
+ mToolbar.invalidate();
+ }
}
};
anim.setDuration(SCRIM_ANIMATION_DURATION);
anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
startAnimation(anim);
+
mScrimIsShown = true;
}
@@ -290,6 +317,13 @@
final int originalAlpha = Color.alpha(mForegroundScrimColor);
mCurrentForegroundColor = ColorUtils.setAlphaComponent(mForegroundScrimColor,
AnimationUtils.lerp(originalAlpha, 0, interpolatedTime));
+
+ // We need to manually invalidate ourselves and the Toolbar to ensure the scrim
+ // is drawn
+ invalidate();
+ if (mToolbar != null) {
+ mToolbar.invalidate();
+ }
}
};
anim.setDuration(SCRIM_ANIMATION_DURATION);
@@ -401,6 +435,15 @@
private static final float DEFAULT_PARALLAX_MULTIPLIER = 0.5f;
+ /** @hide */
+ @IntDef({
+ COLLAPSE_MODE_OFF,
+ COLLAPSE_MODE_PIN,
+ COLLAPSE_MODE_PARALLAX
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface CollapseMode {}
+
/**
* The view will act as normal with no collapsing behavior.
*/
@@ -461,19 +504,42 @@
* @param collapseMode one of {@link #COLLAPSE_MODE_OFF}, {@link #COLLAPSE_MODE_PIN}
* or {@link #COLLAPSE_MODE_PARALLAX}.
*/
- public void setCollapseMode(int collapseMode) {
+ public void setCollapseMode(@CollapseMode int collapseMode) {
mCollapseMode = collapseMode;
}
/**
- * Set the parallax scroll multiplier used in conjuction with
+ * Returns the requested collapse mode.
+ *
+ * @return the current mode. One of {@link #COLLAPSE_MODE_OFF}, {@link #COLLAPSE_MODE_PIN}
+ * or {@link #COLLAPSE_MODE_PARALLAX}.
+ */
+ @CollapseMode
+ public int getCollapseMode() {
+ return mCollapseMode;
+ }
+
+ /**
+ * Set the parallax scroll multiplier used in conjunction with
* {@link #COLLAPSE_MODE_PARALLAX}. A value of {@code 0.0} indicates no movement at all,
* {@code 1.0f} indicates normal scroll movement.
*
* @param multiplier the multiplier.
+ *
+ * @see #getParallaxMultiplier()
*/
public void setParallaxMultiplier(float multiplier) {
mParallaxMult = multiplier;
}
+
+ /**
+ * Returns the parallax scroll multiplier used in conjunction with
+ * {@link #COLLAPSE_MODE_PARALLAX}.
+ *
+ * @see #setParallaxMultiplier(float)
+ */
+ public float getParallaxMultiplier() {
+ return mParallaxMult;
+ }
}
}
diff --git a/design/src/android/support/design/widget/ViewUtils.java b/design/src/android/support/design/widget/ViewUtils.java
new file mode 100644
index 0000000..34fdc68
--- /dev/null
+++ b/design/src/android/support/design/widget/ViewUtils.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.widget;
+
+import android.os.Build;
+import android.view.View;
+
+class ViewUtils {
+
+ private interface ViewUtilsImpl {
+ void setBoundsViewOutlineProvider(View view);
+ }
+
+ private static class ViewUtilsImplBase implements ViewUtilsImpl {
+ @Override
+ public void setBoundsViewOutlineProvider(View view) {
+ // no-op
+ }
+ }
+
+ private static class ViewUtilsImplLollipop implements ViewUtilsImpl {
+ @Override
+ public void setBoundsViewOutlineProvider(View view) {
+ ViewUtilsLollipop.setBoundsViewOutlineProvider(view);
+ }
+ }
+
+ private static final ViewUtilsImpl IMPL;
+
+ static {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 21) {
+ IMPL = new ViewUtilsImplLollipop();
+ } else {
+ IMPL = new ViewUtilsImplBase();
+ }
+ }
+
+ static void setBoundsViewOutlineProvider(View view) {
+ IMPL.setBoundsViewOutlineProvider(view);
+ }
+
+}