效果圖:
1.在觸發down或者move事件的時候,會生成小球,並開始動畫,
2.背景的顏色一致變換的動畫
首先實現背景變換的動畫
private static final int RED = 0xffFF8080;
private static final int BLUE = 0xff8080FF;
private static final int CYAN = 0xff80ffff;
private static final int GREEN = 0xff80ff80;
public MyAnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ValueAnimator anim = ObjectAnimator.ofInt(this,"backgroundColor",RED,BLUE,CYAN,GREEN);
anim.setDuration(3000);
/**ArgbEvaluator兩個顏色之間平緩過度*/
anim.setEvaluator(new ArgbEvaluator());
anim.setRepeatCount(ValueAnimator.INFINITE);
anim.setRepeatMode(ValueAnimator.REVERSE);
anim.start();
}
只許聲明四個顏色,然後利用屬性動畫的:
anim.setRepeatCount(ValueAnimator.INFINITE);
anim.setRepeatMode(ValueAnimator.REVERSE);
這兩行代碼,就能實現動畫循環播放.
在這個裏面很多人肯能對anim.setEvaluator(new ArgbEvaluator())這句代碼心存疑惑,這個Evaluator大家可以直接百度,就知道它的作用了,而在這裏的ArgbEvaluator的作用就是實現漸變色效果,不讓兩個顏色的切換太過唐突,看起來不舒服.
現在我們就來說說小球的生成以及動畫的實現
生成小球在ApiDemo-Animation中是一個很常見的操作,因爲很多動畫都是用小球實現的
private ShapeHolder addBall(float x, float y) {
OvalShape oval = new OvalShape();
oval.resize(50f,50f);
ShapeDrawable drawable = new ShapeDrawable(oval);
ShapeHolder holder = new ShapeHolder(drawable);
holder.setX(x-25f);
holder.setY(y-25f);
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
int color = 0xff000000 | red << 16 | green << 8 | blue;
int darkColor = 0xff000000 | red/4 << 16 | green/4 << 8 | blue/4;
Paint paint = drawable.getPaint();
RadialGradient gradient = new RadialGradient(37.5f,12.5f,50f,color,darkColor, Shader.TileMode.CLAMP);
paint.setShader(gradient);
holder.setGradient(gradient);
holder.setPaint(paint);
balls.add(holder);
return holder;
}
小球的顏色是隨機生成的,ShapeHolder是一個jiavabean
public class ShapeHolder {
private float x = 0 , y = 0;
private ShapeDrawable shape;
private int color;
private RadialGradient gradient;//環形渲染
private float alpha = 1.0f;
private Paint paint;
public ShapeHolder(ShapeDrawable s) {
shape = s;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public ShapeDrawable getShape() {
return shape;
}
public void setShape(ShapeDrawable shape) {
this.shape = shape;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
shape.getPaint().setColor(color);
}
public RadialGradient getGradient() {
return gradient;
}
public void setGradient(RadialGradient gradient) {
this.gradient = gradient;
}
public Paint getPaint() {
return paint;
}
public void setPaint(Paint paint) {
this.paint = paint;
}
public float getAlpha() {
return alpha;
}
public void setAlpha(float alpha) {
this.alpha = alpha;
shape.setAlpha((int) (alpha * 255f + 0.5f));
}
public float getWidth() {
return shape.getShape().getWidth();
}
public void setWidth(float width) {
Shape s = shape.getShape();
s.resize(width, s.getHeight());
}
public float getHeight() {
return shape.getShape().getHeight();
}
public void setHeight(float height) {
Shape s = shape.getShape();
s.resize(s.getWidth(), height);
}
}
這個實體類中保存的就是小球的所有信息了,其中的X,Y指的是小球的左上角的座標,也就是left和top,而並非是小球的圓心座標.
onDraw()
@Override
protected void onDraw(Canvas canvas) {
for (int i = 0; i < balls.size();i++) {
ShapeHolder holder = balls.get(i);
canvas.save();
canvas.translate(holder.getX(),holder.getY());
holder.getShape().draw(canvas);
canvas.restore();
}
}
這裏就是繪製所有的小球了,看起來是相當的簡單,就是移動畫布的位置,然後獲取DrawableShape對象的Draw方法,就成了,canvas.save(),canvas.restore()是要成對使用的,就是保存canvas當前的狀態,特別是位置信息,等完成操作之後,又恢復保存之前的狀態.
那麼在哪創建小球,並調用動畫呢?
onTouchEvent()
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_MOVE){
return false;
}
ShapeHolder newBall = addBall(event.getX(),event.getY());
float startY = newBall.getY();
float h = getHeight();
float endY = getHeight() - 50f;
float eventY = event.getY();
int duration = (int) (500 * ((h - eventY) / h)) * 3;
ValueAnimator bounceAnim = ObjectAnimator.ofFloat(newBall,"y",startY,endY);
bounceAnim.setDuration(duration);
bounceAnim.setInterpolator(new AccelerateInterpolator());
//擠壓
ValueAnimator squashAnim1 = ObjectAnimator.ofFloat(newBall,"x",newBall.getX(),newBall.getX() - 25f);
squashAnim1.setDuration(duration /4);
squashAnim1.setRepeatCount(1);
squashAnim1.setRepeatMode(ValueAnimator.REVERSE);
squashAnim1.setInterpolator(new DecelerateInterpolator());
ValueAnimator squashAnim2 = ObjectAnimator.ofFloat(newBall,"width",newBall.getWidth(),newBall.getWidth() + 50);
squashAnim2.setDuration(duration / 4);
squashAnim2.setRepeatCount(1);
squashAnim2.setRepeatMode(ValueAnimator.REVERSE);
squashAnim2.setInterpolator(new DecelerateInterpolator());
//伸展
ValueAnimator stretchAnim1 = ObjectAnimator.ofFloat(newBall,"y",endY,endY + 25);
stretchAnim1.setDuration(duration / 4);
stretchAnim1.setRepeatCount(1);
stretchAnim1.setRepeatMode(ValueAnimator.REVERSE);
stretchAnim1.setInterpolator(new DecelerateInterpolator());
ValueAnimator stretchAnim2 = ObjectAnimator.ofFloat(newBall,"height",newBall.getHeight(),newBall.getHeight() - 25f);
stretchAnim2.setDuration(duration / 4);
stretchAnim2.setRepeatCount(1);
stretchAnim2.setRepeatMode(ValueAnimator.REVERSE);
stretchAnim2.setInterpolator(new DecelerateInterpolator());
ValueAnimator bounceBackAnim = ObjectAnimator.ofFloat(newBall,"y",endY,startY);
bounceBackAnim.setDuration(duration / 4);
bounceBackAnim.setInterpolator(new DecelerateInterpolator());
//透明度
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall,"alpha",1f,0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
balls.remove(((ObjectAnimator)(animation)).getTarget());
}
});
AnimatorSet set = new AnimatorSet();
set.play(bounceAnim).before(squashAnim1);
set.play(squashAnim1).with(squashAnim2);
set.play(squashAnim1).with(stretchAnim1);
set.play(squashAnim1).with(stretchAnim2);
set.play(bounceBackAnim).after(stretchAnim2);
set.play(bounceBackAnim).before(fadeAnim);
set.start();
return true;
}
首先判斷只有在觸發Down和Move事件時纔會繼續往下走
int action = event.getAction();
if (action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_MOVE){
return false;
}
bounceAnim.setInterpolator(new AccelerateInterpolator())這行代碼是讓小球在下落的過程中做勻加速運動,也就是速度越來越快,
bounceBackAnim.setInterpolator(new DecelerateInterpolator());這行代碼是讓動畫執行的時候,兩邊速度正常,而中間速度變快的運動方式
其實在這個動畫demo中最難的就是小球在下落到最低點的時候,變成橢圓的那個動畫,這裏並不是用一種動畫實現的.而是四個動畫同時進行而完成的這個效果,且這四個動畫的時間必須相同,動畫的Interpolator也必須相同,才能讓動畫渾若一體
這四個動畫的propertyName分別是x,y,width,height.
這裏爲什麼x和width搭配使用.開始有很多初學者並想不明白,這個想明白了那y和height也就能理解了,
如果你單純使用x動畫,那很明顯只是小球向左邊或者右邊移動,要用width,那麼小球的寬度發生變化,但是小球的座標並沒有發生變化,想想都是一種很奇怪的現象,
沒有width
沒有x
很明顯這兩種動畫和效果圖相比是非常詭異的.
效果圖Activity裏面的代碼
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bouncing_balls);
LinearLayout container = (LinearLayout) findViewById(R.id.container);
container.addView(new MyAnimationView(this));
}