|
|
@@ -0,0 +1,249 @@
|
|
|
+package com.itant.behaviordemo;
|
|
|
+
|
|
|
+import android.animation.Animator;
|
|
|
+import android.animation.ValueAnimator;
|
|
|
+import android.content.Context;
|
|
|
+import android.support.annotation.NonNull;
|
|
|
+import android.support.design.widget.AppBarLayout;
|
|
|
+import android.support.design.widget.CoordinatorLayout;
|
|
|
+import android.support.v4.view.ViewCompat;
|
|
|
+import android.support.v4.widget.NestedScrollView;
|
|
|
+import android.util.AttributeSet;
|
|
|
+import android.util.Log;
|
|
|
+import android.view.View;
|
|
|
+
|
|
|
+public class AppBarLayoutOverScrollViewBehavior extends AppBarLayout.Behavior {
|
|
|
+ private static final String TAG = "overScroll";
|
|
|
+ private static final String TAG_BOTTOM = "bottomList";
|
|
|
+ // 最多可以拉伸多长
|
|
|
+ private static final float MAX_EXTRA_HEIGHT = 300;
|
|
|
+ // 回弹动画时长200ms
|
|
|
+ private static final long ANIMATION_DURATION = 350;
|
|
|
+ private View imageView;
|
|
|
+ private boolean isAnimate;
|
|
|
+
|
|
|
+ private float mTotalDy;
|
|
|
+ private boolean isRecovering = false;//是否正在自动回弹中
|
|
|
+
|
|
|
+
|
|
|
+ // 我的
|
|
|
+ private int rawAppbarBottom = 0;
|
|
|
+ private int currentAppbarBottom = 0;
|
|
|
+ private int rawImageHeight = 0;
|
|
|
+ private int currentImageHeight = 0;
|
|
|
+ private int rawAppbarHeight = 0;
|
|
|
+ private int currentAppbarHeight = 0;
|
|
|
+ // 用来标记当前的滑动是不是fling
|
|
|
+ private boolean isFling;
|
|
|
+
|
|
|
+ private AppBarLayout appBarLayout;
|
|
|
+ //private NestedScrollView nestedScrollView;
|
|
|
+
|
|
|
+ public AppBarLayoutOverScrollViewBehavior() {
|
|
|
+ }
|
|
|
+
|
|
|
+ public AppBarLayoutOverScrollViewBehavior(Context context, AttributeSet attrs) {
|
|
|
+ super(context, attrs);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean onLayoutChild(final CoordinatorLayout parent, final AppBarLayout abl, int layoutDirection) {
|
|
|
+ //Log.e(TAG, "onLayoutChild");
|
|
|
+ boolean handled = super.onLayoutChild(parent, abl, layoutDirection);
|
|
|
+ // 需要在调用过super.onLayoutChild()方法之后获取
|
|
|
+ if (imageView == null) {
|
|
|
+ imageView = parent.findViewWithTag(TAG);
|
|
|
+ //nestedScrollView = parent.findViewWithTag(TAG_BOTTOM);
|
|
|
+ if (imageView != null) {
|
|
|
+ initial(abl);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return handled;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) {
|
|
|
+ Log.e(TAG, "onNestedPreFling" + velocityY);
|
|
|
+
|
|
|
+ //当y速度>100,就秒弹回
|
|
|
+ /*if (velocityY > 100) {
|
|
|
+ isAnimate = false;
|
|
|
+ }*/
|
|
|
+ return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean shouldFling = false;
|
|
|
+ float flingVelocityY = 0;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull AppBarLayout child, @NonNull View target, float velocityX, float velocityY, boolean consumed) {
|
|
|
+ Log.e(TAG, "onNestedFling: " + velocityY);
|
|
|
+ isFling = true;
|
|
|
+
|
|
|
+ flingVelocityY = velocityY;
|
|
|
+ return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes, int type) {
|
|
|
+ Log.e(TAG, "start>>>>>>>>>>" + flingVelocityY);
|
|
|
+ if (flingVelocityY < -1000) {
|
|
|
+ flingVelocityY = 0;
|
|
|
+ child.setExpanded(true, true);
|
|
|
+ // 防止fling太用力,一直在最大滑动距离那里停留。
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*if (flingVelocityY > 0 && flingVelocityY < 1000) {
|
|
|
+ // 防止小fling导致底部的NestScrollView在AppBarLayout展开的时候就上滑了。
|
|
|
+ return false;
|
|
|
+ }*/
|
|
|
+
|
|
|
+ // 不能简单地由于是fling就忽略,如果是从折叠状态fling下来的,还是要让其继续下拉的
|
|
|
+ doingCycle = true;
|
|
|
+ if (isFling && child.getBottom() >= rawAppbarBottom) {
|
|
|
+ //Log.e(TAG, "start return false");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ isAnimate = true;
|
|
|
+ if ((nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 || target instanceof DisInterceptNestedScrollView)
|
|
|
+ return true;
|
|
|
+ return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean doingCycle;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {
|
|
|
+ Log.e(TAG, "scrolling.......... " + dy);
|
|
|
+
|
|
|
+ if (imageView != null && !isRecovering && (dy < 0 && child.getBottom() >= rawAppbarHeight)) {
|
|
|
+ if (doingCycle) {
|
|
|
+ if (currentImageHeight >= rawImageHeight + MAX_EXTRA_HEIGHT) {
|
|
|
+ if (!isFling) {
|
|
|
+ scale(child, target, dy);
|
|
|
+ } else {
|
|
|
+ onStopNestedScroll(coordinatorLayout, child, target, type);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ scale(child, target, dy);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ currentImageHeight = rawImageHeight;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target, int type) {
|
|
|
+ Log.e(TAG, "stop<<<<<<<<");
|
|
|
+
|
|
|
+ // 这个方法在拉动的过程中会被频繁调用,如果不处理的话,会造成抖动现象
|
|
|
+ isFling = false;
|
|
|
+ doingCycle = false;
|
|
|
+ recovery(abl);
|
|
|
+ super.onStopNestedScroll(coordinatorLayout, abl, target, type);
|
|
|
+
|
|
|
+ //Log.e(TAG, "onStopNestedScroll: ");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initial(AppBarLayout abl) {
|
|
|
+ appBarLayout = abl;
|
|
|
+
|
|
|
+ abl.setClipChildren(false);
|
|
|
+ rawAppbarBottom = abl.getBottom();
|
|
|
+ currentAppbarBottom = rawAppbarBottom;
|
|
|
+ rawImageHeight = imageView.getHeight();
|
|
|
+ currentImageHeight = rawImageHeight;
|
|
|
+ rawAppbarHeight = abl.getHeight();
|
|
|
+ currentAppbarHeight = rawAppbarHeight;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 拉伸图片
|
|
|
+ */
|
|
|
+ private void scale(AppBarLayout abl, View target, int dy) {
|
|
|
+ currentImageHeight += -dy;
|
|
|
+ currentImageHeight = (int) Math.min(currentImageHeight, rawImageHeight + MAX_EXTRA_HEIGHT);
|
|
|
+ mTotalDy += -dy;
|
|
|
+ mTotalDy = Math.min(mTotalDy, MAX_EXTRA_HEIGHT);
|
|
|
+
|
|
|
+ imageView.getLayoutParams().height = currentImageHeight;
|
|
|
+ imageView.requestLayout();
|
|
|
+
|
|
|
+ currentAppbarHeight = rawAppbarHeight + currentImageHeight - rawImageHeight;
|
|
|
+ abl.setBottom(currentAppbarHeight);
|
|
|
+ target.setScrollY(0);
|
|
|
+
|
|
|
+ ////Log.e(TAG, "dy: " + dy);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 恢复原形
|
|
|
+ */
|
|
|
+ private void recovery(final AppBarLayout abl) {
|
|
|
+ if (isRecovering) return;
|
|
|
+ if (mTotalDy > 0) {
|
|
|
+ isRecovering = true;
|
|
|
+ mTotalDy = 0;
|
|
|
+ if (isAnimate) {
|
|
|
+ ValueAnimator anim = ValueAnimator.ofInt(0, currentImageHeight - rawImageHeight).setDuration(ANIMATION_DURATION);
|
|
|
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
|
|
+ @Override
|
|
|
+ public void onAnimationUpdate(ValueAnimator animation) {
|
|
|
+
|
|
|
+ int value = (int) animation.getAnimatedValue();
|
|
|
+ ////Log.e(TAG, "value: " + value);
|
|
|
+
|
|
|
+ imageView.getLayoutParams().height = currentImageHeight - value;
|
|
|
+ imageView.requestLayout();
|
|
|
+
|
|
|
+ // AppbarLayout逐渐恢复
|
|
|
+ currentAppbarHeight = rawAppbarHeight + currentImageHeight - rawImageHeight - value;
|
|
|
+ appBarLayout.setBottom(currentAppbarHeight);
|
|
|
+ currentAppbarBottom = currentAppbarHeight;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ anim.addListener(new Animator.AnimatorListener() {
|
|
|
+ @Override
|
|
|
+ public void onAnimationStart(Animator animation) {
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAnimationEnd(Animator animation) {
|
|
|
+ isRecovering = false;
|
|
|
+ currentImageHeight = rawImageHeight;
|
|
|
+ currentAppbarHeight = rawAppbarHeight;
|
|
|
+ currentAppbarBottom = rawAppbarBottom;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAnimationCancel(Animator animation) {
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAnimationRepeat(Animator animation) {
|
|
|
+ }
|
|
|
+ });
|
|
|
+ anim.start();
|
|
|
+ } else {
|
|
|
+ isRecovering = false;
|
|
|
+ currentImageHeight = rawImageHeight;
|
|
|
+ currentAppbarHeight = rawAppbarHeight;
|
|
|
+
|
|
|
+ //ViewGroup.LayoutParams params = imageView.getLayoutParams();
|
|
|
+ imageView.getLayoutParams().height = rawImageHeight;
|
|
|
+ imageView.requestLayout();
|
|
|
+
|
|
|
+ abl.setBottom(rawAppbarBottom);
|
|
|
+ currentAppbarBottom = rawAppbarBottom;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|