最近有个需求,需要在view爆炸的动画,搜索了一下,已经有人做到了类似的动画,我参考了下,改进出自己的爆炸效果。
参考文章:blog.csdn.net/crazy__chen/article/details/50149619/
框架基本没变主要是:每个粒子携带自身的运动信息,valueAnimation的getAnimatedValue()来获得每次绘制的时间点,根据时间点来计算当前粒子应该在的位置然后进行绘制。
粒子的类:
package com.example.administrator.viewexplosion.particle;
import android.graphics.*;
import com.example.administrator.viewexplosion.math.Parabola;
import java.util.Random;
/**
* Created by TouTouch on 2016/9/5.
*/
public class RecfParticeNew extends Particle {
static Random random = new Random();
public float alpha;
public Parabola parabola;
public float u0x = 0;//初始速度都设为﹢ 不管左右
public float u0y = 0;//初始速度都设为﹢
public float p0x = 0;//初始X坐标
public float p0y = 0;//初始Y坐标
public boolean isLeft = false;//用来区分左右飞
public float length = 0;//边长
public float life;
public int type = 0;
public float one,two,three,four;
/**
* @param color 颜色
* @param x X坐标
* @param y Y坐标
*/
public RecfParticeNew(int color, float x, float y) {
super(color, x, y);
}
public void init(int color, float x, float y){
this.color = color;
cx = x;
cy = y;
}
public void setParabola(boolean isLeftt ,float ux ,float uy){
parabola = new Parabola(cx , cy , isLeftt , ux , uy);
}
public void getData(){
one = (random.nextFloat() - 0.5f ) * length * 5;
two = (random.nextFloat() - 0.5f) * length * 5;
three = (random.nextFloat() - 0.5f) * length * 5 ;
four = (random.nextFloat() - 0.5f) * length * 5;
}
@Override
protected void draw(Canvas canvas, Paint paint) {
paint.setColor(color);
paint.setAlpha((int) (Color.alpha(color) * alpha)); //这样透明颜色就不是黑色了
if(type == 0){
canvas.drawRect(new RectF(cx , cy , cx + length, cy + length), paint); //画正方形
}else if(type == 1){
canvas.drawRect(new RectF(cx , cy , cx + length, cy + length * 1.5f), paint); //画长方形
}else if(type == 2){
Path path = new Path();
path.moveTo(cx ,cy);
path.lineTo(cx + one ,cy + two);
path.lineTo(cx + three, cy + four);
path.close();
canvas.drawPath(path , paint);
}
}
@Override
protected void caculate(float factor) {
alpha = 1f - factor;
float[] pp = parabola.getNewPosition(factor * 20);
cx = pp[0];
cy = pp[1];
}
}
生产粒子
package com.example.administrator.viewexplosion.factory; import android.graphics.Bitmap; import android.graphics.Rect; import android.util.Log; import com.example.administrator.viewexplosion.particle.Particle; import com.example.administrator.viewexplosion.particle.RecfParticeNew; import java.util.Random; /** * Created by TouTouch on 2016/9/5. */ public class RecfParticleNewFactory extends ParticleFactory{ private static final String TAG = RecfParticleNewFactory.class.getSimpleName(); public static final int PART_WH = 30; //默认宽高 private Rect mBound; public static final float END_VALUE = 1.4f; @Override public Particle[][] generateParticles(Bitmap bitmap, Rect bound) { mBound = new Rect(bound); int w = bound.width(); int h = bound.height(); Random random = new Random(); int partW_Count = w / PART_WH; //横向个数 int partH_Count = h / PART_WH; //竖向个数 int bitmap_part_w = bitmap.getWidth() / partW_Count; int bitmap_part_h = bitmap.getHeight() / partH_Count; // Log.v(TAG ,"mBound.centerX:" + String.valueOf(mBound.centerX())); Particle[][] particles = new Particle[partH_Count][partW_Count]; for (int row = 0; row < partH_Count; row ++) { //行 for (int column = 0; column < partW_Count; column ++) { //列 //取得当前粒子所在位置的颜色 int color = bitmap.getPixel(column * bitmap_part_w, row * bitmap_part_h); float x = bound.left + PART_WH * column; // Log.v(TAG ,"x:" + String.valueOf(x)); float y = bound.top + PART_WH * row; particles[row][column] = generateParticle(color,random , x , y ); } } return particles; } public RecfParticeNew generateParticle(int color , Random random , float x ,float y ){ RecfParticeNew particeNew = new RecfParticeNew(color , x , y); boolean isLeft = true; if(x + 15 >= (mBound.centerX() - (mBound.width() % PART_WH) / 2)){ isLeft = false; } float u0x = 40 * Math.abs(mBound.centerX() - x - 15) / (mBound.width() >> 2) + (random.nextFloat() - 0.5f) * 20; float u0y = (random.nextFloat() - 0.5f) * 40 + (80 - u0x); Log.v(TAG,"x: " + u0x + " y: " + u0y); particeNew.length = random.nextFloat() * 40; particeNew.setParabola(isLeft, u0x, u0y); particeNew.alpha = 1f; particeNew.life = END_VALUE / 10 * random.nextFloat(); particeNew.type = random.nextInt(3); particeNew.getData(); return particeNew; } }
最后,因为在动画太多的事导致APP卡顿,修改了下承载View,改为在SurfaceView上绘制。package com.example.administrator.viewexplosion; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.Activity; import android.content.Context; import android.graphics.*; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.util.AttributeSet; import android.util.Log; import android.view.*; import com.example.administrator.viewexplosion.factory.ParticleFactory; import java.util.ArrayList; import java.util.HashMap; /** * Created by TouTouch on 2016/9/20. */ public class ExplosionFieldSurfaceView extends SurfaceView implements SurfaceHolder.Callback{ private static final String TAG = ExplosionFieldSurfaceView.class.getSimpleName(); private ArrayList<ExplosionAnimator> explosionAnimators; private HashMap<View,ExplosionAnimator> explosionAnimatorsMap; private OnClickListener onClickListener; private ParticleFactory mParticleFactory; protected SurfaceHolder holder; private HandlerThread myThread; public Handler drawHandle; public ExplosionFieldSurfaceView(Context context ,ParticleFactory particleFactory) { super(context); init(particleFactory); } public ExplosionFieldSurfaceView(Context context, AttributeSet attrs,ParticleFactory particleFactory) { super(context, attrs); init(particleFactory); } public ExplosionFieldSurfaceView(Context context, AttributeSet attrs, int defStyleAttr,ParticleFactory particleFactory) { super(context, attrs, defStyleAttr); init(particleFactory); } public void init(ParticleFactory particleFactory){ setZOrderOnTop(true); getHolder().setFormat(PixelFormat.TRANSLUCENT); //使得surfaceView透明,并清除 explosionAnimators = new ArrayList<ExplosionAnimator>(); explosionAnimatorsMap = new HashMap<View,ExplosionAnimator>(); mParticleFactory = particleFactory; attach2Activity((Activity) getContext()); holder = getHolder(); holder.addCallback(this); myThread = new HandlerThread("draw surfaceView"); myThread.start(); drawHandle = new Handler(myThread.getLooper()){ @Override public void handleMessage(Message msg) { // super.handleMessage(msg); Canvas canvas = null; switch (msg.what){ case 0://更新动画 try{ canvas = holder.lockCanvas(); canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); for (ExplosionAnimator animator : explosionAnimators) { animator.draw(canvas); } }catch(Exception e){ Log.v(TAG ,"绘制出错"); }finally { holder.unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像 } break; case 1://清除屏幕 try{ canvas = holder.lockCanvas(); canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); }catch(Exception e){ Log.v(TAG ,"绘制出错"); }finally { holder.unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像 } break; } Log.v(TAG ,"收到绘制消息"); } }; } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { myThread.quitSafely(); }else{ myThread.quit(); } } @Override public void surfaceCreated(SurfaceHolder holder) { // myThread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } /** * 爆破 * @param view 使得该view爆破 */ public void explode(final View view) { //防止重复点击 if(explosionAnimatorsMap.get(view)!=null&&explosionAnimatorsMap.get(view).isStarted()){ return; } if(view.getVisibility()!=View.VISIBLE||view.getAlpha()==0){ return; } final Rect rect = new Rect(); view.getGlobalVisibleRect(rect); //得到view相对于整个屏幕的坐标 int contentTop = ((ViewGroup)getParent()).getTop(); Rect frame = new Rect(); //getWindowVisibleDisplayFrame方法可以获取到程序显示的区域,包括标题栏,但不包括状态栏。 ((Activity) getContext()).getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); int statusBarHeight = frame.top; rect.offset(0, -contentTop - statusBarHeight);//去掉状态栏高度和标题栏高度 if(rect.width()==0||rect.height()==0){ return; } //震动动画 // ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150); // animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { // // Random random = new Random(); // // @Override // public void onAnimationUpdate(ValueAnimator animation) { // view.setTranslationX((random.nextFloat() - 0.5f) * view.getWidth() * 0.05f); // view.setTranslationY((random.nextFloat() - 0.5f) * view.getHeight() * 0.05f); // } // }); // animator.addListener(new AnimatorListenerAdapter() { // @Override // public void onAnimationEnd(Animator animation) { // super.onAnimationEnd(animation); // explode(view, rect); // } // }); explode(view, rect); // animator.start(); } private void explode(final View view,Rect rect) { final ExplosionAnimator animator = new ExplosionAnimator(this, Utils.createBitmapFromView(view), rect,mParticleFactory); explosionAnimators.add(animator); explosionAnimatorsMap.put(view, animator); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { //缩小,透明动画 view.animate().setDuration(150).scaleX(0f).scaleY(0f).alpha(0f).start(); } @Override public void onAnimationEnd(Animator animation) { view.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(150).start(); // view.setVisibility(INVISIBLE); drawHandle.sendEmptyMessageDelayed(1 ,0); //动画结束时从动画集中移除 explosionAnimators.remove(animation); explosionAnimatorsMap.remove(view); animation = null; } }); animator.start(); } /** * 给Activity加上全屏覆盖的ExplosionField */ private void attach2Activity(Activity activity) { ViewGroup rootView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT); ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); rootView.addView(this, lp); } /** * 希望谁有破碎效果,就给谁加Listener * @param view 可以是ViewGroup */ public void addListener(View view) { if (view instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) view; int count = viewGroup.getChildCount(); for (int i = 0 ; i < count; i++) { addListener(viewGroup.getChildAt(i)); } } else { view.setClickable(true); view.setOnClickListener(getOnClickListener()); } } private OnClickListener getOnClickListener() { if (null == onClickListener) { onClickListener = new OnClickListener() { @Override public void onClick(View v) { ExplosionFieldSurfaceView.this.explode(v); } }; } return onClickListener; } }
全部代码在这里,代码下载