283 lines
13 KiB
Java
283 lines
13 KiB
Java
|
package com.example.nanchen.aiyaschoolpush.behavior;
|
|||
|
|
|||
|
import android.content.Context;
|
|||
|
import android.content.res.TypedArray;
|
|||
|
import android.graphics.drawable.ColorDrawable;
|
|||
|
import android.os.Build;
|
|||
|
import android.support.design.widget.AppBarLayout;
|
|||
|
import android.support.design.widget.CoordinatorLayout;
|
|||
|
import android.util.AttributeSet;
|
|||
|
import android.view.View;
|
|||
|
import android.view.animation.Animation;
|
|||
|
import android.view.animation.AnimationUtils;
|
|||
|
import android.view.animation.Transformation;
|
|||
|
|
|||
|
import com.example.nanchen.aiyaschoolpush.R;
|
|||
|
|
|||
|
/**
|
|||
|
* @author nanchen
|
|||
|
* @fileName AiYaSchoolPush
|
|||
|
* @packageName com.example.nanchen.aiyaschoolpush.behavior
|
|||
|
* @date 2016/12/20 14:00
|
|||
|
*/
|
|||
|
|
|||
|
public class SimpleViewBehavior extends CoordinatorLayout.Behavior<View> {
|
|||
|
|
|||
|
private static final int UNSPECIFIED_INT = Integer.MAX_VALUE;
|
|||
|
private static final float UNSPECIFIED_FLOAT = Float.MAX_VALUE;
|
|||
|
|
|||
|
private static final int DEPEND_TYPE_HEIGHT = 0;
|
|||
|
private static final int DEPEND_TYPE_WIDTH = 1;
|
|||
|
private static final int DEPEND_TYPE_X = 2;
|
|||
|
private static final int DEPEND_TYPE_Y = 3;
|
|||
|
|
|||
|
private int mDependViewId = 0; //默认没有依赖对象
|
|||
|
private int mDependType = DEPEND_TYPE_Y; //默认按照y方向变化
|
|||
|
private int mDependTargetX; //X方向的允许最大距离(影响动画percent)
|
|||
|
private int mDependTargetY; //Y方向的允许最大距离(影响动画percent)
|
|||
|
private int mDependTargetWidth; //依赖控件起始最大宽度(影响动画percent)
|
|||
|
private int mDependTargetHeight; //依赖控件起始最大高度(影响动画percent)
|
|||
|
private int targetX;
|
|||
|
private int targetY;
|
|||
|
private int targetWidth;
|
|||
|
private int targetHeight;
|
|||
|
private int targetBackgroundColor;
|
|||
|
private float targetAlpha;
|
|||
|
private float targetRotateX;
|
|||
|
private float targetRotateY;
|
|||
|
private int mAnimationId = 0; //自定义动画id(xml文件定义动画)
|
|||
|
|
|||
|
private int mDependStartX;
|
|||
|
private int mDependStartY;
|
|||
|
private int mDependStartWidth;
|
|||
|
private int mDependStartHeight;
|
|||
|
private int mStartX;
|
|||
|
private int mStartY;
|
|||
|
private int mStartWidth;
|
|||
|
private int mStartHeight;
|
|||
|
private int mStartBackgroundColor;
|
|||
|
private float mStartAlpha;
|
|||
|
private float mStartRotateX;
|
|||
|
private float mStartRotateY;
|
|||
|
|
|||
|
private Animation mAnimation;
|
|||
|
private boolean isPrepared;
|
|||
|
|
|||
|
public SimpleViewBehavior(Context context, AttributeSet attrs) {
|
|||
|
super(context, attrs);
|
|||
|
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SimpleViewBehavior);
|
|||
|
mDependViewId = a.getResourceId(R.styleable.SimpleViewBehavior_svb_dependOn, mDependViewId);
|
|||
|
mDependType = a.getInt(R.styleable.SimpleViewBehavior_svb_dependType, mDependType);
|
|||
|
mDependTargetX = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_dependTargetX, UNSPECIFIED_INT);
|
|||
|
mDependTargetY = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_dependTargetY, UNSPECIFIED_INT);
|
|||
|
mDependTargetWidth = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_dependTargetWidth, UNSPECIFIED_INT);
|
|||
|
mDependTargetHeight = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_dependTargetHeight, UNSPECIFIED_INT);
|
|||
|
targetX = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_targetX, UNSPECIFIED_INT);
|
|||
|
targetY = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_targetY, UNSPECIFIED_INT);
|
|||
|
targetWidth = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_targetWidth, UNSPECIFIED_INT);
|
|||
|
targetHeight = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_targetHeight, UNSPECIFIED_INT);
|
|||
|
targetBackgroundColor = a.getColor(R.styleable.SimpleViewBehavior_svb_targetBackgroundColor, UNSPECIFIED_INT);
|
|||
|
targetAlpha = a.getFloat(R.styleable.SimpleViewBehavior_svb_targetAlpha, UNSPECIFIED_FLOAT);
|
|||
|
targetRotateX = a.getFloat(R.styleable.SimpleViewBehavior_svb_targetRotateX, UNSPECIFIED_FLOAT);
|
|||
|
targetRotateY = a.getFloat(R.styleable.SimpleViewBehavior_svb_targetRotateY, UNSPECIFIED_FLOAT);
|
|||
|
mAnimationId = a.getResourceId(R.styleable.SimpleViewBehavior_svb_animation, mAnimationId);
|
|||
|
a.recycle();
|
|||
|
}
|
|||
|
|
|||
|
/** 初始化数据 */
|
|||
|
private void prepare(CoordinatorLayout parent, View child, View dependency) {
|
|||
|
mDependStartX = (int) dependency.getX();
|
|||
|
mDependStartY = (int) dependency.getY();
|
|||
|
mDependStartWidth = dependency.getWidth();
|
|||
|
mDependStartHeight = dependency.getHeight();
|
|||
|
mStartX = (int) child.getX();
|
|||
|
mStartY = (int) child.getY();
|
|||
|
mStartWidth = child.getWidth();
|
|||
|
mStartHeight = child.getHeight();
|
|||
|
mStartAlpha = child.getAlpha();
|
|||
|
mStartRotateX = child.getRotationX();
|
|||
|
mStartRotateY = child.getRotationY();
|
|||
|
|
|||
|
//特殊处理y方向变化
|
|||
|
if (mDependTargetY == UNSPECIFIED_INT && dependency instanceof AppBarLayout) {
|
|||
|
mDependTargetY = ((AppBarLayout) dependency).getTotalScrollRange();
|
|||
|
}
|
|||
|
// 背景颜色渐变
|
|||
|
if (child.getBackground() instanceof ColorDrawable) mStartBackgroundColor = ((ColorDrawable) child.getBackground()).getColor();
|
|||
|
// 自定义动画
|
|||
|
if (mAnimationId != 0) {
|
|||
|
mAnimation = AnimationUtils.loadAnimation(child.getContext(), mAnimationId);
|
|||
|
mAnimation.initialize(child.getWidth(), child.getHeight(), parent.getWidth(), parent.getHeight());
|
|||
|
}
|
|||
|
// 兼容5.0以上的沉浸模式
|
|||
|
if (Build.VERSION.SDK_INT > 16 && parent.getFitsSystemWindows() && targetY != UNSPECIFIED_INT) {
|
|||
|
targetY += getStatusBarHeight(parent.getContext());
|
|||
|
}
|
|||
|
isPrepared = true;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* child 是指应用behavior的View ,dependency 担任触发behavior的角色,并与child进行互动。
|
|||
|
* layoutDependsOn方法在每次layout发生变化时都会调用,我们需要在dependency控件发生变化时返回True,
|
|||
|
* 在我们的例子中是用户在屏幕上滑动时(因为AppBarLayout发生了移动),然后我们需要让child做出相应的反应。
|
|||
|
*/
|
|||
|
@Override
|
|||
|
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
|
|||
|
return dependency.getId() == mDependViewId;
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
|
|||
|
// 该方法会在滑动的时候一直回调,但只需要初始化一次
|
|||
|
if (!isPrepared) prepare(parent, child, dependency);
|
|||
|
updateView(child, dependency);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 这个是CoordinatorLayout在进行measure的过程中,利用Behavior对象对子view进行大小测量的一个方法。
|
|||
|
* 在这个方法内,我们可以通过parent.getDependencies(child);这个方法,获取到这个child依赖的view,然后通过获取这个child依赖的view的大小来决定自身的大小。
|
|||
|
*/
|
|||
|
@Override
|
|||
|
public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
|
|||
|
return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 这个方法是用来子view用来布局自身使用,如果依赖其他view,那么系统会首先调用
|
|||
|
* public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency)
|
|||
|
* 这个方法,可以在这个回调中记录dependency的一些位置信息,在onLayoutChild中利用保存下来的信息进行计算,然后得到自身的具体位置。
|
|||
|
*/
|
|||
|
@Override
|
|||
|
public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
|
|||
|
boolean bool = super.onLayoutChild(parent, child, layoutDirection);
|
|||
|
if (isPrepared) updateView(child, parent.getDependencies(child).get(0));
|
|||
|
return bool;
|
|||
|
}
|
|||
|
|
|||
|
public void updateView(View child, View dependency) {
|
|||
|
float percent = 0;
|
|||
|
float start = 0;
|
|||
|
float current = 0;
|
|||
|
float end = UNSPECIFIED_INT;
|
|||
|
switch (mDependType) {
|
|||
|
case DEPEND_TYPE_WIDTH:
|
|||
|
start = mDependStartWidth;
|
|||
|
current = dependency.getWidth();
|
|||
|
end = mDependTargetWidth;
|
|||
|
break;
|
|||
|
case DEPEND_TYPE_HEIGHT:
|
|||
|
start = mDependStartHeight;
|
|||
|
current = dependency.getHeight();
|
|||
|
end = mDependTargetHeight;
|
|||
|
break;
|
|||
|
case DEPEND_TYPE_X:
|
|||
|
start = mDependStartX;
|
|||
|
current = dependency.getX();
|
|||
|
end = mDependTargetX;
|
|||
|
break;
|
|||
|
case DEPEND_TYPE_Y:
|
|||
|
start = mDependStartY;
|
|||
|
current = dependency.getY();
|
|||
|
end = mDependTargetY;
|
|||
|
break;
|
|||
|
}
|
|||
|
if (end != UNSPECIFIED_INT) {
|
|||
|
percent = Math.abs(current - start) / Math.abs(end - start);
|
|||
|
}
|
|||
|
updateViewWithPercent(child, percent > 1 ? 1 : percent);
|
|||
|
}
|
|||
|
|
|||
|
/** 更新View */
|
|||
|
@SuppressWarnings("ResourceType")
|
|||
|
public void updateViewWithPercent(View child, float percent) {
|
|||
|
if (mAnimation == null) {
|
|||
|
//如果没有自定义动画,那么使用属性动画
|
|||
|
float newX = targetX == UNSPECIFIED_INT ? 0 : (targetX - mStartX) * percent;
|
|||
|
float newY = targetY == UNSPECIFIED_INT ? 0 : (targetY - mStartY) * percent;
|
|||
|
//缩放动画
|
|||
|
if (targetWidth != UNSPECIFIED_INT || targetHeight != UNSPECIFIED_INT) {
|
|||
|
child.setScaleX(scaleEvaluator(mStartWidth, targetWidth, percent));
|
|||
|
child.setScaleY(scaleEvaluator(mStartHeight, targetHeight, percent));
|
|||
|
float newWidth = floatEvaluator(mStartWidth, targetWidth, percent);
|
|||
|
float newHeight = floatEvaluator(mStartWidth, targetWidth, percent);
|
|||
|
newX -= (mStartWidth - newWidth) / 2;
|
|||
|
newY -= (mStartHeight - newHeight) / 2;
|
|||
|
}
|
|||
|
//平移动画
|
|||
|
child.setTranslationX(newX);
|
|||
|
child.setTranslationY(newY);
|
|||
|
//透明度变化
|
|||
|
if (targetAlpha != UNSPECIFIED_FLOAT) child.setAlpha(floatEvaluator(mStartAlpha, targetAlpha, percent));
|
|||
|
//背景渐变
|
|||
|
if (targetBackgroundColor != UNSPECIFIED_INT && mStartBackgroundColor != 0) {
|
|||
|
child.setBackgroundColor(argbEvaluator(mStartBackgroundColor, targetBackgroundColor, percent));
|
|||
|
}
|
|||
|
//旋转动画
|
|||
|
if (targetRotateX != UNSPECIFIED_FLOAT) child.setRotationX(floatEvaluator(mStartRotateX, targetRotateX, percent));
|
|||
|
if (targetRotateY != UNSPECIFIED_FLOAT) child.setRotationY(floatEvaluator(mStartRotateY, targetRotateY, percent));
|
|||
|
} else {
|
|||
|
mAnimation.setStartTime(0);
|
|||
|
mAnimation.restrictDuration(100);
|
|||
|
Transformation transformation = new Transformation();
|
|||
|
mAnimation.getTransformation((long) (percent * 100), transformation);
|
|||
|
BehaviorAnimation animation = new BehaviorAnimation(transformation);
|
|||
|
child.startAnimation(animation);
|
|||
|
}
|
|||
|
child.requestLayout();
|
|||
|
}
|
|||
|
|
|||
|
private static class BehaviorAnimation extends Animation {
|
|||
|
|
|||
|
private Transformation mTransformation;
|
|||
|
|
|||
|
public BehaviorAnimation(Transformation transformation) {
|
|||
|
mTransformation = transformation;
|
|||
|
setDuration(0);
|
|||
|
setFillAfter(true);
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
protected void applyTransformation(float interpolatedTime, Transformation t) {
|
|||
|
t.compose(mTransformation);
|
|||
|
super.applyTransformation(interpolatedTime, t);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static float floatEvaluator(float originalSize, float finalSize, float percent) {
|
|||
|
return (finalSize - originalSize) * percent + originalSize;
|
|||
|
}
|
|||
|
|
|||
|
public static float scaleEvaluator(float originalSize, float finalSize, float percent) {
|
|||
|
float calcSize = (finalSize - originalSize) * percent + originalSize;
|
|||
|
return calcSize / originalSize;
|
|||
|
}
|
|||
|
|
|||
|
public static int argbEvaluator(int startColor, int endColor, float percent) {
|
|||
|
int startA = (startColor >> 24) & 0xff;
|
|||
|
int startR = (startColor >> 16) & 0xff;
|
|||
|
int startG = (startColor >> 8) & 0xff;
|
|||
|
int startB = startColor & 0xff;
|
|||
|
|
|||
|
int endA = (endColor >> 24) & 0xff;
|
|||
|
int endR = (endColor >> 16) & 0xff;
|
|||
|
int endG = (endColor >> 8) & 0xff;
|
|||
|
int endB = endColor & 0xff;
|
|||
|
|
|||
|
return ((startA + (int) (percent * (endA - startA))) << 24) |
|
|||
|
((startR + (int) (percent * (endR - startR))) << 16) |
|
|||
|
((startG + (int) (percent * (endG - startG))) << 8) |
|
|||
|
((startB + (int) (percent * (endB - startB))));
|
|||
|
}
|
|||
|
|
|||
|
/** 获取状态栏的高度 */
|
|||
|
private static int getStatusBarHeight(Context context) {
|
|||
|
int result = 0;
|
|||
|
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
|
|||
|
if (resourceId > 0) {
|
|||
|
result = context.getResources().getDimensionPixelSize(resourceId);
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
}
|