package com.wangdaye.mysplash.common.ui.widget.nestedScrollView;
import android.animation.ValueAnimator;
import android.content.Context;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingChildHelper;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
/**
* Nested scroll app bar layout.
*
* An AppBarLayout that can dispatch nested scrolling action.
*
* */
@CoordinatorLayout.DefaultBehavior(NestedScrollAppBarLayout.Behavior.class)
public class NestedScrollAppBarLayout extends AppBarLayout
implements NestedScrollingChild {
private NestedScrollingChildHelper nestedScrollingChildHelper;
OnNestedScrollingListener nestedScrollingListener;
private AutomaticScrollAnimator animator;
private float touchSlop;
private float startY;
// an appbar has 3 part : scroll / enterAlways / without scroll flag.
private int scrollHeight;
private int enterAlwaysHeight;
private int staticHeight;
public static class Behavior extends AppBarLayout.Behavior {
private NestedScrollAppBarLayout appBarLayout = null;
private float oldY;
private boolean isBeingDragged;
public Behavior() {
super();
}
public Behavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
private void bindAppBar(AppBarLayout child) {
if (appBarLayout == null) {
this.appBarLayout = (NestedScrollAppBarLayout) child;
}
}
@Override
public boolean onTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
bindAppBar(child);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
appBarLayout.startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
oldY = ev.getY();
isBeingDragged = false;
break;
case MotionEvent.ACTION_MOVE:
if (!isBeingDragged) {
if (Math.abs(ev.getY() - oldY) > appBarLayout.getTouchSlop()) {
isBeingDragged = true;
}
}
if (isBeingDragged) {
int[] total = new int[] {0, (int) (oldY - ev.getY())};
int[] consumed = new int[] {0, 0};
appBarLayout.dispatchNestedPreScroll(
total[0], total[1], consumed, null);
appBarLayout.dispatchNestedScroll(
consumed[0], consumed[1], total[0] - consumed[0], total[1] - consumed[1], null);
}
oldY = ev.getY();
return isBeingDragged;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
appBarLayout.stopNestedScroll();
if (isBeingDragged) {
isBeingDragged = false;
return true;
}
break;
}
return super.onTouchEvent(parent, child, ev);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child,
View directTargetChild, View target, int nestedScrollAxes) {
if (super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes)) {
bindAppBar(child);
if (appBarLayout.nestedScrollingListener != null) {
appBarLayout.nestedScrollingListener.onStartNestedScroll();
}
appBarLayout.stopScrollAnimator();
appBarLayout.setStartY(child.getY());
return true;
} else {
return false;
}
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
bindAppBar(child);
if (appBarLayout.nestedScrollingListener != null) {
appBarLayout.nestedScrollingListener.onNestedScrolling();
}
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(
coordinatorLayout, child, target,
dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
bindAppBar(child);
if (appBarLayout.nestedScrollingListener != null) {
appBarLayout.nestedScrollingListener.onNestedScrolling();
}
}
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);
bindAppBar(child);
if (appBarLayout.nestedScrollingListener != null) {
appBarLayout.nestedScrollingListener.onStopNestedScroll();
}
float top = child.getY();
float height = child.getMeasuredHeight();
float bottom = top + height;
appBarLayout.computerHeightData();
if (appBarLayout.scrollHeight > 0 || appBarLayout.enterAlwaysHeight > 0) {
if (appBarLayout.getStartY() == top) {
return;
} if (appBarLayout.getStartY() > top) { // drag up.
appBarLayout.hideTopBar();
} else if (appBarLayout.getStartY() < top) { // drag down.
if (bottom > appBarLayout.enterAlwaysHeight + appBarLayout.staticHeight) {
appBarLayout.showTopBar();
} else if (bottom > appBarLayout.staticHeight) {
appBarLayout.showEnterAlwaysBar();
}
}
}
}
}
private class AutomaticScrollAnimator extends ValueAnimator {
private CoordinatorLayout.Behavior behavior = null;
private int lastY;
AutomaticScrollAnimator(final int toY) {
final ViewGroup.LayoutParams params = getLayoutParams();
if (params instanceof CoordinatorLayout.LayoutParams) {
behavior = ((CoordinatorLayout.LayoutParams) params).getBehavior();
}
final int fromY = (int) getY();
this.lastY = fromY;
setIntValues(fromY, toY);
setDuration((long) (150.0 + 150.0 * Math.abs(toY - fromY) / getMeasuredHeight()));
setInterpolator(new DecelerateInterpolator());
addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (behavior != null) {
int newY = (int) animation.getAnimatedValue();
int[] total = new int[] {0, lastY - newY};
int[] consumed = new int[] {0, 0};
behavior.onNestedPreScroll(
(CoordinatorLayout) getParent(), NestedScrollAppBarLayout.this, null,
total[0], total[1], consumed);
behavior.onNestedScroll(
(CoordinatorLayout) getParent(), NestedScrollAppBarLayout.this, null,
consumed[0], consumed[1], total[0] - consumed[0], total[1] - consumed[1]);
lastY = newY;
}
}
});
}
}
public NestedScrollAppBarLayout(Context context) {
super(context);
this.initialize();
}
public NestedScrollAppBarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.initialize();
}
private void initialize() {
this.nestedScrollingChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
this.touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
/**
* Do animation to expand the whole AppBarLayout.
* */
public void showTopBar() {
stopScrollAnimator();
doScrollAnimation(0);
}
/**
* Do animation to expand the part of AppBarLayout which has "enterAlways" flag.
* */
public void showEnterAlwaysBar() {
stopScrollAnimator();
doScrollAnimation(-scrollHeight);
}
/**
* Do animation to hide the part of AppBarLayout which has "scroll" flag.
* */
public void hideTopBar() {
stopScrollAnimator();
doScrollAnimation(staticHeight - getMeasuredHeight());
}
private void doScrollAnimation(int toY) {
if (getY() != toY) {
this.animator = new AutomaticScrollAnimator(toY);
animator.start();
}
}
public void stopScrollAnimator() {
if (animator != null) {
animator.cancel();
}
}
public float getTouchSlop() {
return touchSlop;
}
public float getStartY() {
return startY;
}
public void setStartY(float startY) {
this.startY = startY;
}
/**
* compute the height of three part in AppBarLayout.
* */
void computerHeightData() {
scrollHeight = enterAlwaysHeight = staticHeight = 0;
for (int i = 0; i < getChildCount(); i ++) {
View v = getChildAt(i);
LayoutParams params = (LayoutParams) v.getLayoutParams();
int flags = params.getScrollFlags();
if ((flags & LayoutParams.SCROLL_FLAG_SNAP) == LayoutParams.SCROLL_FLAG_SNAP) {
scrollHeight = enterAlwaysHeight = staticHeight = 0;
return;
} else if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != LayoutParams.SCROLL_FLAG_SCROLL) {
staticHeight += v.getMeasuredHeight();
} else if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS) == LayoutParams.SCROLL_FLAG_ENTER_ALWAYS) {
enterAlwaysHeight += v.getMeasuredHeight();
} else {
scrollHeight += v.getMeasuredHeight();
}
}
}
// interface.
// on nested scrolling listener.
public interface OnNestedScrollingListener {
void onStartNestedScroll();
void onNestedScrolling();
void onStopNestedScroll();
}
public void setOnNestedScrollingListener(OnNestedScrollingListener l) {
this.nestedScrollingListener = l;
}
// nested scrolling child.
@Override
public void setNestedScrollingEnabled(boolean enabled) {
nestedScrollingChildHelper.setNestedScrollingEnabled(enabled);
}
@Override
public boolean isNestedScrollingEnabled() {
return nestedScrollingChildHelper.isNestedScrollingEnabled();
}
@Override
public boolean startNestedScroll(int axes) {
return nestedScrollingChildHelper.startNestedScroll(axes);
}
@Override
public void stopNestedScroll() {
nestedScrollingChildHelper.stopNestedScroll();
}
@Override
public boolean hasNestedScrollingParent() {
return nestedScrollingChildHelper.hasNestedScrollingParent();
}
@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
return nestedScrollingChildHelper.dispatchNestedScroll(
dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, offsetInWindow);
}
@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return nestedScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
@Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}
@Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
}