一、粒子属性
粒子图片,粒子大小,粒子缩放比例,粒子位置(X,Y),粒子移动方向与速度(X,Y坐标与时间t的关系)
计算粒子所处位置,绘制渲染
粒子系统,是大量基础图元构成的具有一定规律的不规律渲染图像。即要够成粒子系统,需要有基本图元,保持基本图元的一定规律性,并细化不规律性。
一个基本图元即是粒子系统中展示的一个粒子,有以下基本属性:粒子纹理【图片】,粒子的大小,粒子缩放,粒子的位置【平面坐标系中只有X,Y】,粒子移动方向与速度(X,Y坐标与时间T的关系)。
粒子属性基本信息保持一致,在各个变量细化时加入随机变量震动,从而实现统一大规律下不统一。
准备好所有粒子信息后,先检查粒子当前所处的位置,超出范围部分强制修改,然后绘制渲染粒子。
二、控件实现
构造方法,初始化基本数据:
public ParticleView(Context context) {
super(context);
}
public ParticleView(Context context, AttributeSet attrs) {
super(context, attrs);
initData(context);
initBitmapInfo();
initPaint();
}
public ParticleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 初始化绘制笔
*/
private void initPaint() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 防抖动
paint.setDither(true);
// 开启图像过滤
paint.setFilterBitmap(true);
}
/**
* 初始化粒子纹理
*/
private void initBitmapInfo() {
mStarOne = ((BitmapDrawable) mResources.getDrawable(R.drawable.star2)).getBitmap();
// mStarOne = ((BitmapDrawable) mResources.getDrawable(R.drawable.snow1)).getBitmap();
mStarOneWidth = mStarOne.getWidth();
mStarOneHeight = mStarOne.getHeight();
// mStarTwo = ((BitmapDrawable) mResources.getDrawable(R.drawable.star1)).getBitmap();
mStarTwo = ((BitmapDrawable) mResources.getDrawable(R.drawable.snow2)).getBitmap();
mStarTwoWidth = mStarTwo.getWidth();
mStarTwoHeight = mStarTwo.getHeight();
// mStarThree = ((BitmapDrawable) mResources.getDrawable(R.drawable.star3)).getBitmap();
mStarThree = ((BitmapDrawable) mResources.getDrawable(R.drawable.snow3)).getBitmap();
mStarThreeWidth = mStarThree.getWidth();
mStarThreeHeight = mStarThree.getHeight();
}
/**
* 初始化数据
*
* @param context
*/
private void initData(Context context) {
mResources = getResources();
DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();
mTotalWidth = dm.widthPixels;
mTotalHeight = dm.heightPixels;
Log.i(TAG, "mTotalWidth=" + mTotalWidth + "--1--mTotalHeight=" + mTotalHeight);
//设置三个不同大小的速度值
mFloatTransLowSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.5f,
mResources.getDisplayMetrics());
mFloatTransMidSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.75f,
mResources.getDisplayMetrics());
mFloatTransFastSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f,
mResources.getDisplayMetrics());
}
控件在展示中,生命周期变化,影响控件展示效果,重新计算控件的基本属性。
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mCenterX = mTotalWidth / 2;
mCenterY = mTotalHeight / 2;
mSrcRect = new Rect();
mDestRect = new Rect(0, 0, mTotalWidth, mTotalHeight);
mStarOneSrcRect = new Rect(0, 0, mStarOneWidth, mStarOneHeight);
mStarTwoSrcRect = new Rect(0, 0, mStarTwoWidth, mStarTwoHeight);
mStarThreeSrcRect = new Rect(0, 0, mStarThreeWidth, mStarThreeHeight);
Log.i(TAG, "mTotalWidth=" + mTotalWidth + "---2-mTotalHeight=" + mTotalHeight);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
initParticleInfo();
}
/**
* 初始化所有粒子数据
*/
private void initParticleInfo() {
Particle starInfo = null;
Random random = new Random();
for (int i = 0; i < mFloatCount; i++) {
// 获取星星大小比例
float starSize = initParticleSize(0.4f, 0.8f);
//小球的坐标
float[] starLocation = STAR_LOCATION[i];
starInfo = new Particle();
// 初始化星星大小
starInfo.sizePercent = starSize;
// 初始化漂浮速度
int randomSpeed = random.nextInt(3);
switch (randomSpeed) {
case 0:
starInfo.speed = mFloatTransLowSpeed;
break;
case 1:
starInfo.speed = mFloatTransMidSpeed;
break;
case 2:
starInfo.speed = mFloatTransFastSpeed;
break;
default:
starInfo.speed = mFloatTransMidSpeed;
break;
}
// 初始化星星透明度
starInfo.alpha = initParticleSize(0.3f, 0.8f);
// 初始化星星位置
starInfo.xLocation = (int) (starLocation[0] * mTotalWidth);
starInfo.yLocation = (int) (starLocation[1] * mTotalHeight);
Log.i(TAG, "xLocation = " + starInfo.xLocation + "--yLocation = "
+ starInfo.yLocation);
Log.i(TAG, "stoneSize = " + starSize + "---stoneAlpha = "
+ starInfo.alpha);
// 初始化星星位置
starInfo.direction = getParticleDirection();
mStarInfos.add(starInfo);
}
}
/**
* 获取粒子当前运动方向
*
* @return
*/
private int getParticleDirection() {
int randomInt;
Random random = new Random();
if (floatTyep == 100) {
randomInt = random.nextInt(3);
} else {
randomInt = floatTyep;
}
int direction = 0;
switch (randomInt) {
case 0:
direction = LEFT;
break;
case 1:
direction = RIGHT;
break;
case 2:
direction = TOP;
break;
case 3:
direction = BOTTOM;
break;
case 4:
direction = FREE_POINT;
break;
default:
break;
}
return direction;
}
/**
* 对粒子进行缩放
*
* @param start 缩放最小值
* @param end 缩放最大值
* @return
*/
private float initParticleSize(float start, float end) {
float nextFloat = (float) Math.random();
if (start < nextFloat && nextFloat < end) {
return nextFloat;
} else {
// 如果不处于想要的数据段,则再随机一次,因为不断递归有风险
return (float) Math.random();
}
}
绘制粒子系统:【先检查粒子位置,再渲染】
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < mStarInfos.size(); i++) {
Particle starInfo = mStarInfos.get(i);
drawParticleDynamic(i, starInfo, canvas, paint);
}
}
private void drawParticleDynamic(int count, Particle starInfo,
Canvas canvas, Paint paint) {
//绘制前重置区域
resetParticleFloat(starInfo);
float starAlpha = starInfo.alpha;
int xLocation = starInfo.xLocation;
int yLocation = starInfo.yLocation;
float sizePercent = starInfo.sizePercent;
xLocation = (int) (xLocation / sizePercent);
yLocation = (int) (yLocation / sizePercent);
Bitmap bitmap = null;
Rect srcRect = null;
Rect destRect = new Rect();
mStarOneSrcRect = new Rect(0, 0, mStarOneWidth, mStarOneHeight);
if (count % 3 == 0) {
bitmap = mStarOne;
srcRect = mStarOneSrcRect;
destRect.set(xLocation, yLocation,
xLocation + mStarOneWidth, yLocation
+ mStarOneHeight);
} else if (count % 2 == 0) {
bitmap = mStarThree;
srcRect = mStarThreeSrcRect;
destRect.set(xLocation, yLocation, xLocation
+ mStarThreeWidth, yLocation + mStarThreeHeight);
} else {
bitmap = mStarTwo;
srcRect = mStarTwoSrcRect;
destRect.set(xLocation, yLocation, xLocation
+ mStarTwoWidth, yLocation + mStarTwoHeight);
}
paint.setAlpha((int) (starAlpha * 255));
canvas.save();
canvas.scale(sizePercent, sizePercent);
canvas.drawBitmap(bitmap, srcRect, destRect, paint);
canvas.restore();
}
/**
* 区域外检测
*
* @param starInfo
*/
private void resetParticleFloat(Particle starInfo) {
switch (starInfo.direction) {
case LEFT:
if (starInfo.xLocation < -20) {
starInfo.xLocation = mTotalWidth;
} else {
starInfo.xLocation -= starInfo.speed;
}
break;
case RIGHT:
if (starInfo.xLocation > mTotalWidth + 20) {
starInfo.xLocation = 0;
} else {
starInfo.xLocation += starInfo.speed;
}
break;
case TOP:
if (starInfo.yLocation < -20) {
starInfo.yLocation = mTotalHeight;
} else {
starInfo.yLocation -= starInfo.speed;
}
break;
case BOTTOM:
if (starInfo.yLocation > mTotalHeight + 30) {
starInfo.yLocation = 0;
} else {
starInfo.yLocation += starInfo.speed;
}
break;
case FREE_POINT://斜线运动,可扩展成为曲线运动
if (starInfo.yLocation > mTotalHeight + 30) {
starInfo.yLocation = 0;
} else {
starInfo.yLocation += starInfo.speed;
}
if (starInfo.xLocation < -20) {
starInfo.xLocation = mTotalWidth;
} else {
starInfo.xLocation -= starInfo.speed;
}
break;
default:
break;
}
}
管理控制动画的开始结束:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (isRuning) {
postInvalidate();
handler.sendMessageDelayed(Message.obtain(), 50);
}
}
};
public void startAnimationFloat() {
isRuning = true;
handler.sendMessage(Message.obtain());
}
public void stopAnimationFloat() {
isRuning = false;
}
public void restartAnimationFloat() {
startAnimationFloat();
}
public void setFloatType(int floatType) {
this.floatTyep = floatType;
}
三、展示效果
叠加效果【扩展背景】
使用:
public class ShowActivity extends Activity {
/**
* 粒子系统
*/
private ParticleView particleView;
/**
* 粒子系统运动类型
*/
private int type = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show);
particleView = (ParticleView) findViewById(R.id.particle_view);
try {//防止无传参跳入
type = getIntent().getIntExtra("type", 100);
} catch (Exception e) {
e.printStackTrace();
}
particleView.setFloatType(ParticleView.DEFAULT_TYPE);
switch (type) {
case 1:
particleView.setFloatType(ParticleView.DEFAULT_TYPE);
break;
case 2:
particleView.setFloatType(ParticleView.FREE_POINT);
break;
case 3:
particleView.setFloatType(ParticleView.TOP);
break;
case 4:
particleView.setFloatType(ParticleView.BOTTOM);
break;
case 5:
particleView.setFloatType(ParticleView.LEFT);
break;
case 6:
particleView.setFloatType(ParticleView.RIGHT);
break;
}
particleView.startAnimationFloat();
}
}
源码
百花开时我不开,我若开时百花杀,若与西风战一场,满城尽带黄金甲!