mirror of
https://github.com/material-components/material-components-android.git
synced 2026-02-20 08:39:55 +08:00
Merge "Use StateListAnimator for AppBarLayout elevation changes" into nyc-dev
GitOrigin-RevId=cb849a71706ac0660a5383c11629bc8d9c1a9f28 PiperOrigin-RevId: 140560610
This commit is contained in:
parent
ac75324568
commit
a03cca4fb7
@ -4,12 +4,12 @@ package android.support.design.widget {
|
||||
ctor public AppBarLayout(android.content.Context);
|
||||
ctor public AppBarLayout(android.content.Context, android.util.AttributeSet);
|
||||
method public void addOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
|
||||
method public float getTargetElevation();
|
||||
method public deprecated float getTargetElevation();
|
||||
method public final int getTotalScrollRange();
|
||||
method public void removeOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
|
||||
method public void setExpanded(boolean);
|
||||
method public void setExpanded(boolean, boolean);
|
||||
method public void setTargetElevation(float);
|
||||
method public deprecated void setTargetElevation(float);
|
||||
}
|
||||
|
||||
public static class AppBarLayout.Behavior extends android.support.design.widget.HeaderBehavior {
|
||||
|
||||
@ -16,13 +16,65 @@
|
||||
|
||||
package android.support.design.widget;
|
||||
|
||||
import android.animation.AnimatorInflater;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.StateListAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.support.design.R;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
|
||||
class ViewUtilsLollipop {
|
||||
|
||||
private static final int[] STATE_LIST_ANIM_ATTRS = new int[] {android.R.attr.stateListAnimator};
|
||||
|
||||
static void setBoundsViewOutlineProvider(View view) {
|
||||
view.setOutlineProvider(ViewOutlineProvider.BOUNDS);
|
||||
}
|
||||
|
||||
static void setStateListAnimatorFromAttrs(View view, AttributeSet attrs,
|
||||
int defStyleAttr, int defStyleRes) {
|
||||
final Context context = view.getContext();
|
||||
final TypedArray a = context.obtainStyledAttributes(attrs, STATE_LIST_ANIM_ATTRS,
|
||||
defStyleAttr, defStyleRes);
|
||||
try {
|
||||
if (a.hasValue(0)) {
|
||||
StateListAnimator sla = AnimatorInflater.loadStateListAnimator(context,
|
||||
a.getResourceId(0, 0));
|
||||
view.setStateListAnimator(sla);
|
||||
}
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and sets a {@link StateListAnimator} with a custom elevation value
|
||||
*/
|
||||
static void setDefaultAppBarLayoutStateListAnimator(final View view,
|
||||
final float targetElevation) {
|
||||
final StateListAnimator sla = new StateListAnimator();
|
||||
|
||||
// Enabled, collapsible and collapsed == elevated
|
||||
sla.addState(new int[]{android.R.attr.enabled, R.attr.state_collapsible,
|
||||
R.attr.state_collapsed},
|
||||
ObjectAnimator.ofFloat(view, "elevation", targetElevation));
|
||||
|
||||
// Enabled and collapsible, but not collapsed != elevated
|
||||
sla.addState(new int[]{android.R.attr.enabled, R.attr.state_collapsible,
|
||||
-R.attr.state_collapsed},
|
||||
ObjectAnimator.ofFloat(view, "elevation", 0f));
|
||||
|
||||
// Enabled but not collapsible == elevated
|
||||
sla.addState(new int[]{android.R.attr.enabled, -R.attr.state_collapsible},
|
||||
ObjectAnimator.ofFloat(view, "elevation", targetElevation));
|
||||
|
||||
// Default, none elevated state
|
||||
sla.addState(new int[0], ObjectAnimator.ofFloat(view, "elevation", 0));
|
||||
|
||||
view.setStateListAnimator(sla);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
43
res/anim-v21/design_appbar_state_list_animator.xml
Normal file
43
res/anim-v21/design_appbar_state_list_animator.xml
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:state_enabled="true" app:state_collapsible="true" app:state_collapsed="true">
|
||||
<objectAnimator android:propertyName="elevation"
|
||||
android:valueTo="@dimen/design_appbar_elevation"
|
||||
android:valueType="floatType"/>
|
||||
</item>
|
||||
|
||||
<item android:state_enabled="true" app:state_collapsible="true" app:state_collapsed="false">
|
||||
<objectAnimator android:propertyName="elevation"
|
||||
android:valueTo="0dp"
|
||||
android:valueType="floatType"/>
|
||||
</item>
|
||||
|
||||
<item android:state_enabled="true" app:state_collapsible="false">
|
||||
<objectAnimator android:propertyName="elevation"
|
||||
android:valueTo="@dimen/design_appbar_elevation"
|
||||
android:valueType="floatType"/>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<objectAnimator android:propertyName="elevation"
|
||||
android:valueTo="0"
|
||||
android:valueType="floatType"/>
|
||||
</item>
|
||||
|
||||
</selector>
|
||||
24
res/values-v21/styles.xml
Normal file
24
res/values-v21/styles.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2016 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.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<style name="Widget.Design.AppBarLayout" parent="Base.Widget.Design.AppBarLayout">
|
||||
<item name="android:stateListAnimator">@anim/design_appbar_state_list_animator</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@ -210,6 +210,7 @@
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="AppBarLayout">
|
||||
<!-- Deprecated. Elevation is now controlled via a state list animator. -->
|
||||
<attr name="elevation" />
|
||||
<attr name="android:background" />
|
||||
<!-- The initial expanded state for the AppBarLayout. This only takes effect when this
|
||||
@ -217,6 +218,15 @@
|
||||
<attr name="expanded" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="AppBarLayoutStates">
|
||||
<!-- State value for {@link android.support.design.widget.AppBarLayout} set when the view
|
||||
has been collapsed. -->
|
||||
<attr name="state_collapsed" format="boolean" />
|
||||
<!-- State value for {@link android.support.design.widget.AppBarLayout} set when the view
|
||||
has children which are capable of being collapsed. -->
|
||||
<attr name="state_collapsible" format="boolean" />
|
||||
</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
|
||||
|
||||
@ -101,11 +101,13 @@
|
||||
<item name="statusBarScrim">?attr/colorPrimaryDark</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Design.AppBarLayout" parent="android:Widget">
|
||||
<item name="elevation">@dimen/design_appbar_elevation</item>
|
||||
<style name="Base.Widget.Design.AppBarLayout" parent="android:Widget">
|
||||
<item name="android:background">?attr/colorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Design.AppBarLayout" parent="Base.Widget.Design.AppBarLayout">
|
||||
</style>
|
||||
|
||||
<style name="Widget.Design.CoordinatorLayout" parent="android:Widget">
|
||||
<item name="statusBarBackground">?attr/colorPrimaryDark</item>
|
||||
</style>
|
||||
|
||||
@ -18,6 +18,7 @@ package android.support.design.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.IntDef;
|
||||
@ -124,15 +125,18 @@ public class AppBarLayout extends LinearLayout {
|
||||
private int mDownPreScrollRange = INVALID_SCROLL_RANGE;
|
||||
private int mDownScrollRange = INVALID_SCROLL_RANGE;
|
||||
|
||||
boolean mHaveChildWithInterpolator;
|
||||
|
||||
private float mTargetElevation;
|
||||
private boolean mHaveChildWithInterpolator;
|
||||
|
||||
private int mPendingAction = PENDING_ACTION_NONE;
|
||||
|
||||
private WindowInsetsCompat mLastInsets;
|
||||
|
||||
private final List<OnOffsetChangedListener> mListeners;
|
||||
private List<OnOffsetChangedListener> mListeners;
|
||||
|
||||
private boolean mCollapsible;
|
||||
private boolean mCollapsed;
|
||||
|
||||
private final int[] mTmpStatesArray = new int[2];
|
||||
|
||||
public AppBarLayout(Context context) {
|
||||
this(context, null);
|
||||
@ -144,22 +148,29 @@ public class AppBarLayout extends LinearLayout {
|
||||
|
||||
ThemeUtils.checkAppCompatTheme(context);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout,
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
// Use the bounds view outline provider so that we cast a shadow, even without a
|
||||
// background
|
||||
ViewUtilsLollipop.setBoundsViewOutlineProvider(this);
|
||||
|
||||
// If we're running on API 21+, we should reset any state list animator from our
|
||||
// default style
|
||||
ViewUtilsLollipop.setStateListAnimatorFromAttrs(this, attrs, 0,
|
||||
R.style.Widget_Design_AppBarLayout);
|
||||
}
|
||||
|
||||
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout,
|
||||
0, R.style.Widget_Design_AppBarLayout);
|
||||
mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0);
|
||||
setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background));
|
||||
if (a.hasValue(R.styleable.AppBarLayout_expanded)) {
|
||||
setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false));
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 21 && a.hasValue(R.styleable.AppBarLayout_elevation)) {
|
||||
ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(
|
||||
this, 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);
|
||||
|
||||
mListeners = new ArrayList<>();
|
||||
|
||||
ViewCompat.setElevation(this, mTargetElevation);
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(this,
|
||||
new android.support.v4.view.OnApplyWindowInsetsListener() {
|
||||
@Override
|
||||
@ -178,6 +189,9 @@ public class AppBarLayout extends LinearLayout {
|
||||
* @see #removeOnOffsetChangedListener(OnOffsetChangedListener)
|
||||
*/
|
||||
public void addOnOffsetChangedListener(OnOffsetChangedListener listener) {
|
||||
if (mListeners == null) {
|
||||
mListeners = new ArrayList<>();
|
||||
}
|
||||
if (listener != null && !mListeners.contains(listener)) {
|
||||
mListeners.add(listener);
|
||||
}
|
||||
@ -189,7 +203,7 @@ public class AppBarLayout extends LinearLayout {
|
||||
* @param listener the listener to remove.
|
||||
*/
|
||||
public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) {
|
||||
if (listener != null) {
|
||||
if (mListeners != null && listener != null) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
@ -216,6 +230,19 @@ public class AppBarLayout extends LinearLayout {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateCollapsible();
|
||||
}
|
||||
|
||||
private void updateCollapsible() {
|
||||
boolean haveCollapsibleChild = false;
|
||||
for (int i = 0, z = getChildCount(); i < z; i++) {
|
||||
if (((LayoutParams) getChildAt(i).getLayoutParams()).isCollapsible()) {
|
||||
haveCollapsibleChild = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setCollapsible(haveCollapsibleChild);
|
||||
}
|
||||
|
||||
private void invalidateScrollRanges() {
|
||||
@ -422,6 +449,19 @@ public class AppBarLayout extends LinearLayout {
|
||||
return mDownScrollRange = Math.max(0, range);
|
||||
}
|
||||
|
||||
private void dispatchOffsetUpdates(int offset) {
|
||||
// Iterate backwards through the list so that most recently added listeners
|
||||
// get the first chance to decide
|
||||
if (mListeners != null) {
|
||||
for (int i = 0, z = mListeners.size(); i < z; i++) {
|
||||
final OnOffsetChangedListener listener = mListeners.get(i);
|
||||
if (listener != null) {
|
||||
listener.onOffsetChanged(this, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final int getMinimumHeightForVisibleOverlappingContent() {
|
||||
final int topInset = getTopInset();
|
||||
final int minHeight = ViewCompat.getMinimumHeight(this);
|
||||
@ -437,28 +477,54 @@ public class AppBarLayout extends LinearLayout {
|
||||
: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the elevation value to use when this {@link AppBarLayout} should be elevated
|
||||
* above content.
|
||||
* <p>
|
||||
* This method does not do anything itself. A typical use for this method is called from within
|
||||
* an {@link OnOffsetChangedListener} when the offset has changed in such a way to require an
|
||||
* elevation change.
|
||||
*
|
||||
* @param elevation the elevation value to use.
|
||||
*
|
||||
* @see ViewCompat#setElevation(View, float)
|
||||
*/
|
||||
public void setTargetElevation(float elevation) {
|
||||
mTargetElevation = elevation;
|
||||
@Override
|
||||
protected int[] onCreateDrawableState(int extraSpace) {
|
||||
final int[] extraStates = mTmpStatesArray;
|
||||
final int[] states = super.onCreateDrawableState(extraSpace + extraStates.length);
|
||||
|
||||
extraStates[0] = mCollapsible ? R.attr.state_collapsible : -R.attr.state_collapsible;
|
||||
extraStates[1] = mCollapsible && mCollapsed
|
||||
? R.attr.state_collapsed : -R.attr.state_collapsed;
|
||||
|
||||
return mergeDrawableStates(states, extraStates);
|
||||
}
|
||||
|
||||
private void setCollapsible(boolean collapsible) {
|
||||
if (mCollapsible != collapsible) {
|
||||
mCollapsible = collapsible;
|
||||
refreshDrawableState();
|
||||
}
|
||||
}
|
||||
|
||||
private void setCollapsed(boolean collapsed) {
|
||||
if (mCollapsed != collapsed) {
|
||||
mCollapsed = collapsed;
|
||||
refreshDrawableState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the elevation value to use when this {@link AppBarLayout} should be elevated
|
||||
* above content.
|
||||
* @deprecated target elevation is now deprecated. AppBarLayout's elevation is now
|
||||
* controlled via a {@link StateListAnimator}. If a target elevation is set, either by this
|
||||
* method or the {@code app:elevation} attibute, a new state list animator is created which
|
||||
* uses the given {@code elevation} value.
|
||||
*
|
||||
* @attr ref android.support.design.R.styleable#AppBarLayout_elevation
|
||||
*/
|
||||
@Deprecated
|
||||
public void setTargetElevation(float elevation) {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(this, elevation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated target elevation is now deprecated. AppBarLayout's elevation is now
|
||||
* controlled via a {@link StateListAnimator}. This method now always returns 0.
|
||||
*/
|
||||
@Deprecated
|
||||
public float getTargetElevation() {
|
||||
return mTargetElevation;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int getPendingAction() {
|
||||
@ -550,6 +616,8 @@ public class AppBarLayout extends LinearLayout {
|
||||
*/
|
||||
static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS;
|
||||
static final int FLAG_SNAP = SCROLL_FLAG_SCROLL | SCROLL_FLAG_SNAP;
|
||||
static final int COLLAPSIBLE_FLAGS = SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
|
||||
| SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED;
|
||||
|
||||
int mScrollFlags = SCROLL_FLAG_SCROLL;
|
||||
Interpolator mScrollInterpolator;
|
||||
@ -643,6 +711,14 @@ public class AppBarLayout extends LinearLayout {
|
||||
public Interpolator getScrollInterpolator() {
|
||||
return mScrollInterpolator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the scroll flags are compatible for 'collapsing'
|
||||
*/
|
||||
private boolean isCollapsible() {
|
||||
return (mScrollFlags & SCROLL_FLAG_SCROLL) == SCROLL_FLAG_SCROLL
|
||||
&& (mScrollFlags & COLLAPSIBLE_FLAGS) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -938,8 +1014,8 @@ public class AppBarLayout extends LinearLayout {
|
||||
setTopAndBottomOffset(
|
||||
MathUtils.constrain(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0));
|
||||
|
||||
// Make sure we update the elevation
|
||||
dispatchOffsetUpdates(abl);
|
||||
// Make sure we dispatch the offset update
|
||||
abl.dispatchOffsetUpdates(getTopAndBottomOffset());
|
||||
|
||||
return handled;
|
||||
}
|
||||
@ -995,7 +1071,7 @@ public class AppBarLayout extends LinearLayout {
|
||||
? interpolateOffset(appBarLayout, newOffset)
|
||||
: newOffset;
|
||||
|
||||
boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset);
|
||||
final boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset);
|
||||
|
||||
// Update how much dy we have consumed
|
||||
consumed = curOffset - newOffset;
|
||||
@ -1011,7 +1087,11 @@ public class AppBarLayout extends LinearLayout {
|
||||
}
|
||||
|
||||
// Dispatch the updates to any listeners
|
||||
dispatchOffsetUpdates(appBarLayout);
|
||||
appBarLayout.dispatchOffsetUpdates(getTopAndBottomOffset());
|
||||
|
||||
// Update the AppBarLayout's drawable state (for any elevation changes)
|
||||
updateAppBarLayoutDrawableState(appBarLayout, newOffset,
|
||||
newOffset < curOffset ? -1 : 1);
|
||||
}
|
||||
} else {
|
||||
// Reset the offset delta
|
||||
@ -1021,19 +1101,6 @@ public class AppBarLayout extends LinearLayout {
|
||||
return consumed;
|
||||
}
|
||||
|
||||
private void dispatchOffsetUpdates(AppBarLayout layout) {
|
||||
final List<OnOffsetChangedListener> listeners = layout.mListeners;
|
||||
|
||||
// Iterate backwards through the list so that most recently added listeners
|
||||
// get the first chance to decide
|
||||
for (int i = 0, z = listeners.size(); i < z; i++) {
|
||||
final OnOffsetChangedListener listener = listeners.get(i);
|
||||
if (listener != null) {
|
||||
listener.onOffsetChanged(layout, getTopAndBottomOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int interpolateOffset(AppBarLayout layout, final int offset) {
|
||||
final int absOffset = Math.abs(offset);
|
||||
|
||||
@ -1081,6 +1148,44 @@ public class AppBarLayout extends LinearLayout {
|
||||
return offset;
|
||||
}
|
||||
|
||||
private void updateAppBarLayoutDrawableState(final AppBarLayout layout,
|
||||
final int offset, final int direction) {
|
||||
final View child = getAppBarChildOnOffset(layout, offset);
|
||||
if (child != null) {
|
||||
final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
|
||||
final int flags = childLp.getScrollFlags();
|
||||
boolean collapsed = false;
|
||||
|
||||
if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
|
||||
final int minHeight = ViewCompat.getMinimumHeight(child);
|
||||
|
||||
if (direction > 0 && (flags & (LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
|
||||
| LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED)) != 0) {
|
||||
// We're set to enter always collapsed so we are only collapsed when
|
||||
// being scrolled down, and in a collapsed offset
|
||||
collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset();
|
||||
} else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
|
||||
// We're set to exit until collapsed, so any offset which results in
|
||||
// the minimum height (or less) being shown is collapsed
|
||||
collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset();
|
||||
}
|
||||
}
|
||||
|
||||
layout.setCollapsed(collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
private static View getAppBarChildOnOffset(final AppBarLayout layout, final int offset) {
|
||||
final int absOffset = Math.abs(offset);
|
||||
for (int i = 0, z = layout.getChildCount(); i < z; i++) {
|
||||
final View child = layout.getChildAt(i);
|
||||
if (absOffset >= child.getTop() && absOffset <= child.getBottom()) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
int getTopBottomOffsetForScrollingSibling() {
|
||||
return getTopAndBottomOffset() + mOffsetDelta;
|
||||
|
||||
@ -1117,7 +1117,6 @@ public class CollapsingToolbarLayout extends FrameLayout {
|
||||
mCurrentOffset = verticalOffset;
|
||||
|
||||
final int insetTop = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
|
||||
final int scrollRange = layout.getTotalScrollRange();
|
||||
|
||||
for (int i = 0, z = getChildCount(); i < z; i++) {
|
||||
final View child = getChildAt(i);
|
||||
@ -1151,15 +1150,6 @@ public class CollapsingToolbarLayout extends FrameLayout {
|
||||
CollapsingToolbarLayout.this) - insetTop;
|
||||
mCollapsingTextHelper.setExpansionFraction(
|
||||
Math.abs(verticalOffset) / (float) expandRange);
|
||||
|
||||
if (Math.abs(verticalOffset) == scrollRange) {
|
||||
// If we have some pinned children, and we're offset to only show those views,
|
||||
// we want to be elevate
|
||||
ViewCompat.setElevation(layout, layout.getTargetElevation());
|
||||
} else {
|
||||
// Otherwise, we're inline with the content
|
||||
ViewCompat.setElevation(layout, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
package android.support.design.widget;
|
||||
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
class ViewUtils {
|
||||
|
||||
@ -31,39 +30,6 @@ 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);
|
||||
}
|
||||
|
||||
static ValueAnimatorCompat createAnimator() {
|
||||
return DEFAULT_ANIMATOR_CREATOR.createAnimator();
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
|
||||
package android.support.design.widget;
|
||||
|
||||
import android.os.Build;
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.support.annotation.LayoutRes;
|
||||
@ -27,6 +28,7 @@ import android.support.test.espresso.action.CoordinatesProvider;
|
||||
import android.support.test.espresso.action.GeneralSwipeAction;
|
||||
import android.support.test.espresso.action.Press;
|
||||
import android.support.test.espresso.action.Swipe;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
@ -38,6 +40,8 @@ import static android.support.design.testutils.TestUtilsActions.setTitle;
|
||||
import static android.support.test.espresso.Espresso.onView;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public abstract class AppBarLayoutBaseTest extends BaseDynamicCoordinatorLayoutTest {
|
||||
|
||||
protected AppBarLayout mAppBar;
|
||||
@ -48,6 +52,8 @@ public abstract class AppBarLayoutBaseTest extends BaseDynamicCoordinatorLayoutT
|
||||
|
||||
protected TextView mTextView;
|
||||
|
||||
protected float mDefaultElevationValue;
|
||||
|
||||
protected static void performVerticalSwipeUpGesture(@IdRes int containerId, final int swipeX,
|
||||
final int swipeStartY, final int swipeAmountY) {
|
||||
onView(withId(containerId)).perform(new GeneralSwipeAction(
|
||||
@ -113,5 +119,14 @@ public abstract class AppBarLayoutBaseTest extends BaseDynamicCoordinatorLayoutT
|
||||
onView(withId(R.id.textview_dialogue)).perform(
|
||||
setText(TextUtils.concat(Shakespeare.DIALOGUE)));
|
||||
}
|
||||
|
||||
mDefaultElevationValue = mAppBar.getResources()
|
||||
.getDimensionPixelSize(R.dimen.design_appbar_elevation);
|
||||
}
|
||||
|
||||
protected void assertAppBarElevation(float expectedValue) {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
assertEquals(expectedValue, ViewCompat.getElevation(mAppBar), 0.05f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,6 +51,8 @@ public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
|
||||
final int longSwipeAmount = 3 * appbarHeight / 2;
|
||||
final int shortSwipeAmount = toolbarHeight;
|
||||
|
||||
assertAppBarElevation(0f);
|
||||
|
||||
// Perform a swipe-up gesture across the horizontal center of the screen.
|
||||
performVerticalSwipeUpGesture(
|
||||
R.id.coordinator_layout,
|
||||
@ -74,6 +76,7 @@ public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
|
||||
// At this point the app bar should still be visually snapped below the system status bar
|
||||
// as it is in the pinned mode. Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop + toolbarHeight, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform a short swipe-down gesture across the horizontal center of the screen.
|
||||
// Note that the swipe down is a bit longer than the swipe up to check that the app bar
|
||||
@ -89,6 +92,7 @@ public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
|
||||
// as it is in the pinned mode and we haven't fully swiped down the content below the
|
||||
// app bar. Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop + toolbarHeight, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform another swipe-down gesture across the horizontal center of the screen.
|
||||
performVerticalSwipeDownGesture(
|
||||
@ -102,6 +106,7 @@ public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
|
||||
// Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(0f);
|
||||
|
||||
// Perform yet another swipe-down gesture across the horizontal center of the screen.
|
||||
performVerticalSwipeDownGesture(
|
||||
@ -115,6 +120,7 @@ public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
|
||||
// Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -141,6 +147,8 @@ public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
|
||||
final int longSwipeAmount = 3 * appbarHeight / 2;
|
||||
final int shortSwipeAmount = toolbarHeight;
|
||||
|
||||
assertAppBarElevation(0f);
|
||||
|
||||
// Perform a swipe-up gesture across the horizontal center of the screen.
|
||||
performVerticalSwipeUpGesture(
|
||||
R.id.coordinator_layout,
|
||||
@ -153,6 +161,7 @@ public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
|
||||
// edge aligned with the system status bar.
|
||||
// Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(0f);
|
||||
|
||||
// Perform another swipe-up gesture
|
||||
performVerticalSwipeUpGesture(
|
||||
@ -165,6 +174,7 @@ public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
|
||||
// At this point the app bar should still be off the screen. Allow for off-by-a-pixel
|
||||
// margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(0f);
|
||||
|
||||
// Perform a short swipe-down gesture across the horizontal center of the screen.
|
||||
// Note that the swipe down is a bit longer than the swipe up to fully bring down
|
||||
@ -180,6 +190,7 @@ public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
|
||||
// in scrolling mode and we've swiped down, but not fully. Allow for off-by-a-pixel
|
||||
// margin of error.
|
||||
assertEquals(originalAppbarTop + toolbarHeight, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform another swipe-down gesture across the horizontal center of the screen.
|
||||
performVerticalSwipeDownGesture(
|
||||
@ -193,6 +204,7 @@ public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
|
||||
// Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1]);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight);
|
||||
assertAppBarElevation(0f);
|
||||
|
||||
// Perform yet another swipe-down gesture across the horizontal center of the screen.
|
||||
performVerticalSwipeDownGesture(
|
||||
@ -206,6 +218,7 @@ public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
|
||||
// Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -76,6 +76,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// At this point the app bar should not be visually "present" on the screen, with its bottom
|
||||
// edge aligned with the system status bar. Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform another swipe-up gesture
|
||||
performVerticalSwipeUpGesture(
|
||||
@ -88,6 +89,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// At this point the app bar should still be off the screen. Allow for off-by-a-pixel
|
||||
// margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform a long swipe-down gesture across the horizontal center of the screen.
|
||||
// Note that the swipe down is a bit longer than the swipe up to fully bring down
|
||||
@ -104,6 +106,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform another swipe-down gesture across the horizontal center of the screen.
|
||||
performVerticalSwipeDownGesture(
|
||||
@ -117,6 +120,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform yet another swipe-down gesture across the horizontal center of the screen.
|
||||
performVerticalSwipeDownGesture(
|
||||
@ -130,6 +134,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -163,6 +168,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// At this point the tab bar should be visually snapped below the system status bar.
|
||||
// Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop + tabsHeight, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform another swipe-up gesture
|
||||
performVerticalSwipeUpGesture(
|
||||
@ -175,6 +181,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// At this point the tab bar should still be visually snapped below the system status bar
|
||||
// as it is in the pinned mode. Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop + tabsHeight, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform a short swipe-down gesture across the horizontal center of the screen.
|
||||
// Note that the swipe down is a bit longer than the swipe up to fully bring down
|
||||
@ -191,6 +198,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform another swipe-down gesture across the horizontal center of the screen.
|
||||
performVerticalSwipeDownGesture(
|
||||
@ -204,6 +212,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform yet another swipe-down gesture across the horizontal center of the screen.
|
||||
performVerticalSwipeDownGesture(
|
||||
@ -217,6 +226,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@ -264,6 +274,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform a slightly longer swipe-up gesture, this time by 75% of the toolbar height.
|
||||
// We expect the snap behavior to move the app bar to snap the tab layout below the
|
||||
@ -281,6 +292,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// At this point the app bar should "snap" the toolbar away and align the tab layout below
|
||||
// the system status bar. Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop + tabsHeight, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform a short swipe-up gesture, this time by 25% of the tab layout height. We expect
|
||||
// snap behavior to move the app bar back to snap the tab layout below the system status
|
||||
@ -298,6 +310,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// At this point the app bar should "snap" back to align the tab layout below
|
||||
// the system status bar. Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop + tabsHeight, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform a longer swipe-up gesture, this time by 75% of the tab layout height. We expect
|
||||
// snap behavior to move the app bar fully away from the screen.
|
||||
@ -314,6 +327,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// At this point the app bar should not be visually "present" on the screen, with its bottom
|
||||
// edge aligned with the system status bar. Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform a short swipe-down gesture by 25% of the tab layout height. We expect
|
||||
// snap behavior to move the app bar back fully away from the screen.
|
||||
@ -331,6 +345,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// its bottom edge aligned with the system status bar. Allow for off-by-a-pixel margin
|
||||
// of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform a longer swipe-up gesture, this time by 75% of the tab layout height. We expect
|
||||
// snap behavior to move the app bar to snap the tab layout below the system status
|
||||
@ -348,6 +363,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// At this point the app bar should "snap" the toolbar away and align the tab layout below
|
||||
// the system status bar. Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop + tabsHeight, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform a short swipe-down gesture by 25% of the toolbar height. We expect
|
||||
// snap behavior to align the tab layout below the system status bar
|
||||
@ -364,6 +380,7 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// At this point the app bar should still align the tab layout below
|
||||
// the system status bar. Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop + tabsHeight, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
|
||||
// Perform a longer swipe-up gesture, this time by 75% of the toolbar height. We expect
|
||||
// snap behavior to move the app bar back to its original place (fully visible).
|
||||
@ -381,5 +398,6 @@ public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
|
||||
// Allow for off-by-a-pixel margin of error.
|
||||
assertEquals(originalAppbarTop, appbarOnScreenXY[1], 1);
|
||||
assertEquals(originalAppbarBottom, appbarOnScreenXY[1] + appbarHeight, 1);
|
||||
assertAppBarElevation(mDefaultElevationValue);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user