am b27dc24f: am ec2310cd: Merge "Add ability to snap AppBarLayout children to edges" into mnc-ub-dev

* commit 'b27dc24f1e66228c02e96ce9014f6b1ef7e024c8':
  Add ability to snap AppBarLayout children to edges

GitOrigin-RevId=ab7326eb4c724c7ab61cb71f890a5fe6a1d18fc6

PiperOrigin-RevId: 140550061
This commit is contained in:
Chris Banes 2016-11-30 01:31:50 +00:00 committed by Travis Collins
parent cfffaeecb6
commit 928cc599c2
3 changed files with 71 additions and 26 deletions

View File

@ -49,6 +49,7 @@ package android.support.design.widget {
field public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 8; // 0x8
field public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 2; // 0x2
field public static final int SCROLL_FLAG_SCROLL = 1; // 0x1
field public static final int SCROLL_FLAG_SNAP = 16; // 0x10
}
public static abstract interface AppBarLayout.OnOffsetChangedListener {

View File

@ -207,6 +207,10 @@
reached the end of it's scroll range, the remainder of this view will be scrolled
into view. -->
<flag name="enterAlwaysCollapsed" value="0x8"/>
<!-- Upon a scroll ending, if the view is only partially visible then it will be
snapped and scrolled to it's closest edge. -->
<flag name="snap" value="0x10"/>
</attr>
<!-- An interpolator to use when scrolling this View. Only takes effect when View

View File

@ -492,7 +492,8 @@ public class AppBarLayout extends LinearLayout {
SCROLL_FLAG_SCROLL,
SCROLL_FLAG_EXIT_UNTIL_COLLAPSED,
SCROLL_FLAG_ENTER_ALWAYS,
SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED
SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED,
SCROLL_FLAG_SNAP
})
@Retention(RetentionPolicy.SOURCE)
public @interface ScrollFlags {}
@ -532,9 +533,18 @@ public class AppBarLayout extends LinearLayout {
public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8;
/**
* Internal flag which allows quick checking of 'quick return'
* Upon a scroll ending, if the view is only partially visible then it will be snapped
* and scrolled to it's closest edge. For example, if the view only has it's bottom 25%
* displayed, it will be scrolled off screen completely. Conversely, if it's bottom 75%
* is visible then it will be scrolled fully into view.
*/
public static final int SCROLL_FLAG_SNAP = 0x10;
/**
* Internal flags which allows quick checking features
*/
static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS;
static final int FLAG_SNAP = SCROLL_FLAG_SCROLL | SCROLL_FLAG_SNAP;
int mScrollFlags = SCROLL_FLAG_SCROLL;
Interpolator mScrollInterpolator;
@ -582,8 +592,8 @@ public class AppBarLayout extends LinearLayout {
* Set the scrolling flags.
*
* @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL},
* {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS}
* and {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED}.
* {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS},
* {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED} and {@link #SCROLL_FLAG_SNAP }.
*
* @see #getScrollFlags()
*
@ -641,6 +651,7 @@ public class AppBarLayout extends LinearLayout {
private int mOffsetDelta;
private boolean mSkipNestedPreScroll;
private boolean mWasFlung;
private Runnable mFlingRunnable;
private ScrollerCompat mScroller;
@ -719,10 +730,16 @@ public class AppBarLayout extends LinearLayout {
}
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl,
View target) {
// Reset the skip flag
if (!mWasFlung) {
// If we haven't been flung then let's see if the current view has been set to snap
snapToChildIfNeeded(coordinatorLayout, abl);
}
// Reset the flags
mSkipNestedPreScroll = false;
mWasFlung = false;
// Keep a reference to the previous nested scrolling child
mLastNestedScrollingChildRef = new WeakReference<>(target);
}
@ -840,41 +857,39 @@ public class AppBarLayout extends LinearLayout {
public boolean onNestedFling(final CoordinatorLayout coordinatorLayout,
final AppBarLayout child, View target, float velocityX, float velocityY,
boolean consumed) {
boolean flung = false;
if (!consumed) {
// It has been consumed so let's fling ourselves
return fling(coordinatorLayout, child, -child.getTotalScrollRange(), 0, -velocityY);
flung = fling(coordinatorLayout, child, -child.getTotalScrollRange(),
0, -velocityY);
} else {
// If we're scrolling up and the child also consumed the fling. We'll fake scroll
// upto our 'collapsed' offset
int targetScroll;
if (velocityY < 0) {
// We're scrolling down
targetScroll = -child.getTotalScrollRange()
final int targetScroll = -child.getTotalScrollRange()
+ child.getDownNestedPreScrollRange();
if (getTopBottomOffsetForScrollingSibling() > targetScroll) {
// If we're currently expanded more than the target scroll, we'll return false
// now. This is so that we don't 'scroll' the wrong way.
return false;
if (getTopBottomOffsetForScrollingSibling() < targetScroll) {
// If we're currently not expanded more than the target scroll, we'll
// animate a fling
animateOffsetTo(coordinatorLayout, child, targetScroll);
flung = true;
}
} else {
// We're scrolling up
targetScroll = -child.getUpNestedPreScrollRange();
if (getTopBottomOffsetForScrollingSibling() < targetScroll) {
// If we're currently expanded less than the target scroll, we'll return
// false now. This is so that we don't 'scroll' the wrong way.
return false;
final int targetScroll = -child.getUpNestedPreScrollRange();
if (getTopBottomOffsetForScrollingSibling() > targetScroll) {
// If we're currently not expanded less than the target scroll, we'll
// animate a fling
animateOffsetTo(coordinatorLayout, child, targetScroll);
flung = true;
}
}
if (getTopBottomOffsetForScrollingSibling() != targetScroll) {
animateOffsetTo(coordinatorLayout, child, targetScroll);
return true;
}
}
return false;
mWasFlung = flung;
return flung;
}
private void animateOffsetTo(final CoordinatorLayout coordinatorLayout,
@ -923,6 +938,31 @@ public class AppBarLayout extends LinearLayout {
}
}
private View getChildOnOffset(AppBarLayout abl, final int offset) {
for (int i = 0, count = abl.getChildCount(); i < count; i++) {
View child = abl.getChildAt(i);
if (child.getTop() <= -offset && child.getBottom() >= -offset) {
return child;
}
}
return null;
}
private void snapToChildIfNeeded(CoordinatorLayout coordinatorLayout, AppBarLayout abl) {
final int offset = getTopBottomOffsetForScrollingSibling();
final View offsetChild = getChildOnOffset(abl, offset);
if (offsetChild != null) {
final LayoutParams lp = (LayoutParams) offsetChild.getLayoutParams();
if ((lp.getScrollFlags() & LayoutParams.FLAG_SNAP) == LayoutParams.FLAG_SNAP) {
// We're set the snap, so animate the offset to the nearest edge
final int childTop = -offsetChild.getTop();
final int childBottom = -offsetChild.getBottom();
animateOffsetTo(coordinatorLayout, abl,
offset < (childBottom + childTop) / 2 ? childBottom : childTop);
}
}
}
private class FlingRunnable implements Runnable {
private final CoordinatorLayout mParent;
private final AppBarLayout mLayout;