ApiDemo-BouncingBalls

效果圖:
這裏寫圖片描述

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指的是小球的左上角的座標,也就是lefttop,而並非是小球的圓心座標.

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

首先判斷只有在觸發DownMove事件時纔會繼續往下走

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.
這裏爲什麼xwidth搭配使用.開始有很多初學者並想不明白,這個想明白了那yheight也就能理解了,

如果你單純使用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));

 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章