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;
|
||
}
|
||
}
|