Android 动画之属性动画(三)#
Animation Listeners##
前面的两节中都或多或少提到了动画监听 Animation Listeners 这个概念。它用来监听动画的过程,并且提供一些接口让你实现一些逻辑。比如说你想在动画开始的时候进行一些操作,就需要为这个动画设置一个监听器,然后实现你的逻辑,这样,当监听器监听到动画开始,就回去实现你的逻辑
Animator.AnimatorListener
Animator 类当中提供了一个 addListener() 方法,这个方法接收一个接口类 AnimatorListener ,获得该接口后就可以重写以下四个方法:
- onAnimationStart() - 当动画开始的时候调用
- onAnimationEnd() - 当动画结束后调用
- onAnimationRepeat() - 当动画重复播放时调用
- onAnimationCancel() - 当动画被取消的时候调用,并且在调用完这个方法后,会自动调用 onAnimationEnd() 方法
下面的例子中为每一段代码实现了一个逻辑:弹出对应的文字
anim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
Toast.makeText(MainActivity.this, "start", Toast.LENGTH_LONG).show();
}
@Override
public void onAnimationEnd(Animator animator) {
Toast.makeText(MainActivity.this, "End", Toast.LENGTH_LONG).show();
}
@Override
public void onAnimationCancel(Animator animator) {
Toast.makeText(MainActivity.this, "Cancel", Toast.LENGTH_LONG).show();
}
@Override
public void onAnimationRepeat(Animator animator) {
Toast.makeText(MainActivity.this, "rapeat", Toast.LENGTH_LONG).show();
}
});
anim.start();
//添加一个按钮,按钮的功能用于cancel动画
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
anim.cancel();
}
});
来看运行后效果
按下cancel键后
当然如果你不想把所有方法都重写的话,可以使用 AnimatorListenerAdapter 类,如下
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
}
});
这样的话就可以选择自己想要重写方法进行重写
ValueAnimator.AnimatorUpdateListener
Animator 类当中提供了一个 addUpdateListener() 方法,这个方法接收一个接口类 AnimatorUpdateListener ,获得该接口后就可以重写 onAnimationUpdate() 方法,这个方法为动画提供每一帧的监听,之前在(一)的时候提到,每一帧其实是用一个 value 值来控制的,所以通常情况下我们都需要在每一帧监听的时候获得这个 value 值,就需要调用 ValueAnimator.getAnimatedValue() 方法来获得。
ValueAnimator vanim = ValueAnimator.ofInt(0,10,20);
vanim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//如果之前的ValueAnimtor指定的是Int的i话,那么返回的Value就是int类型,
也就是返回值类型与你创建的类型一致
int value = (int) valueAnimator.getAnimatedValue();
}
});
Animating Layout Changes to ViewGroups##
属性动画不仅可以让你对 view 类进行动画操作,同时它还可以用于 viewgroup 类的变化进行动画操作。比如说你有一个 gridLayout ,你需要在里面添加按钮,那么按钮加到 gridLayout 是一个动画的过程,系统会有一个默认的动画。如果想要改变这个默认的动画效果,就可以借助 LayoutTransition 类来为这个 gridLayout 修改动画效果。使用步骤:
- 新建一个 LayoutTransition
- 对 LayoutTransition 通过调用 setAnimator ( int transitionType, Animator animator ) 设置自定义的动画
- 为所需要的 ViewGroup 通过 setLayoutTransition( LayoutTransition ) 添加一个 LayoutTransition
需要注意的是在setAnimator()中的第一个参数 transitionType 决定了动画的类型,一共有四种:
-
APPEARING:元素在容器中显现时需要动画显示
-
CHANGE_APPEARING:由于容器中要显现一个新的元素,其它元素的变化需要动画显示
-
DISAPPEARING:元素在容器中消失时需要动画显示
-
CHANGE_DISAPPEARING:由于容器中某个元素要消失,其它元素的变化需要动画显示
代码: LayoutTransition layoutTransition = new LayoutTransition(); //设置这个动画为缩放 ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(null,"scaleX",0f,1f); layoutTransition.setAnimator(LayoutTransition.APPEARING,objectAnimator); gridContainer.setLayoutTransition(layoutTransition); //通过点击添加按钮,往gridContainer添加button,这里就涉及到了上面第一种类型的动画:appear add_button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Button newButton = new Button(MainActivity.this); newButton.setText(String.valueOf(num++)); gridContainer.addView(newButton, Math.min(1, gridContainer.getChildCount())); } });
可以看到,按钮出现的时候是以缩放的形式出现的
更多关于 LayoutTransition 请查看文档
https://developer.android.com/reference/android/animation/LayoutTransition.html
Using a TypeEvaluator
在第一篇的时候我们就有讲到 Evaluator 这个类。请复习 Android 动画之属性动画(一)>Evaluator 类。有些时候你需要返回的动画返回的 value 不是系统默认的 Int,Float 或 Color 类型的,那么你就可以自己自定义这个 Evaluator 来返回你所需要的类型!比如说一个坐标点。这也是 属性动画 比 补间动画 强大的精髓所在。
那要如何自己实现呢,首先需要继承 TypeEvaluator 接口,然后重写里面的 evaluate() 方法。
//其实FloatEvaluator也是继承TypeEvaluator,来实现的
public class FloatEvaluator implements TypeEvaluator {
//注意看到fraction参数,在第一篇里面提到的参数
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}
下面我们来实现一个返回一个坐标点的 Evaluator
首先新建一个点类:Point
public class Point {
private float x;
private float y;
public Point(float x,float y) {
this.x = x;
this.y = y;
}
public float getX(){
return x;
}
public float getY(){
return y;
}
}
然后我们新建一个 PointEvaluator ,在里面要实现的是当我们传入一个初始点和结束点,然后实现平缓从初始点到结束点
public class PointEvaluator implements TypeEvaluator {
float x;
float y;
@Override
public Object evaluate(float fraction, Object s, Object e) {
Point startPoint = (Point) s;
Point endPoint = (Point) e;
float x = startPoint.getX()+fraction*(endPoint.getX()-startPoint.getX());
float y = startPoint.getY()+fraction*(endPoint.getY()-startPoint.getY());
Point point = new Point(x,y);
return point;
}
}
然后我们新建一个view,在view里面有一个绘制一个圆,通过对圆心坐标的动画实现球的动画
代码的思路就是在左上角画一个圆,然后对圆心坐标进行进行动画,为动画监听,每得到一个新的圆心坐标就重新绘制view,下面代码略长,耐心看是很容易看懂的
public class myView extends View {
Paint mpaint;
Point CurrentPoint = null;
float radius;
public myView(Context context) {
this(context, null);
}
public myView(Context context, AttributeSet attrs) {
super(context, attrs);
mpaint = new Paint();
mpaint.setStyle(Paint.Style.FILL);
mpaint.setColor(Color.BLUE);
radius = 25f;
}
//CurrentPoint用来记录当前圆心的位置,用来告诉canvas在哪绘制圆,Currentpoint为空时
则新建一个坐标点,然后设置动画
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (CurrentPoint == null) {
CurrentPoint = new Point(radius, radius);
drawCircle(canvas);
setAnimation();
} else {
drawCircle(canvas);
}
}
//根据当前坐标画圆
private void drawCircle(Canvas canvas) {
canvas.drawCircle(CurrentPoint.getX(), CurrentPoint.getY(), radius, mpaint);
}
//设置动画。每当有新的point返,则通知view重绘
public void setAnimation() {
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(),
new Point(radius,radius),
new Point(getWidth() - radius, getHeight() - radius));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
CurrentPoint = (Point) valueAnimator.getAnimatedValue();
invalidate();
}
});
valueAnimator.setDuration(2000);
valueAnimator.start();
}
}
到此我们已经完成了自定义的 Evaluator ,请自行运行查看效果
Using Interpolators##
前面已经提到过 Interpolators 的作用,这里不再重复。如果你没有找到适合自己的 Interpolators ,那么就可以继承 Interpolators 然后重写 getInterpolation()
方法
以下是系统实现 AccelerateDecelerateInterpolator 和 LinearInterpolator 的算法:
AccelerateDecelerateInterpolator
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
LinearInterpolator
public float getInterpolation(float input) {
return input;
}
网友评论