根据原文
http://blog.csdn.net/u014608640/article/details/52487653
基础上修改, 模拟碰撞, 可随机颜色, 气泡模糊等
private static final int MSG_CREATE_BUBBLE = 0;
public static final int MAX_LEVEL = 100;
public static final int MIN_LEVEL = 0;
protected float mWidth;
protected float mHeight;
//底部填充色
protected int mColor = 0xFF61d66b;
//气泡上升高度(水面高度)
protected float mTop;
//底部 == mHeight
protected float mBottom;
protected Paint mPaint;
// 0 - 100 mTop根据此值计算
protected int mLevel = 100;
//存放气泡集合
private List mBubbles = new CopyOnWriteArrayList<>();
private Random mRandom = new Random();
private boolean mStarting = false;
private Paint mBubblePaint;
//气泡随机颜色值
private String[] mBubbleColors = {
"#ffec5c",
"#00ccff",
"#ffffff"
};
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
@Override
protected final void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
//计算top
mTop = mHeight - (int) (mHeight * (mLevel *1f / MAX_LEVEL));
mBottom = mHeight;
//颜色填充
//top
RectF topRectF = new RectF(0, 0, mWidth, mTop);
mPaint.setAlpha(50);
canvas.drawRect(topRectF, mPaint);
//bottom
RectF bottomRectF = new RectF(0, mTop, mWidth, mBottom);
mPaint.setAlpha(255);
canvas.drawRect(bottomRectF, mPaint);
//画气泡
drawBubble(canvas);
invalidate();
}
气泡实体类
private class Bubble {
/** 气泡半径 */
private float radius;
/** 上升速度 */
private float speedY;
/** 平移速度 */
private float speedX;
/** 气泡x坐标 */
private float x;
/** 气泡y坐标 */
private float y;
/** 距离顶部消失距离 */
private float removeY;
/** 渐变色 */
private int[] shaderColor;
/* 原使色 */
private int originalColor;
public float getRadius() {
return radius;
}
public void setRadius(float radius) {
this.radius = radius;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setRemoveY(float y) {
this.removeY = y;
}
public float getRemoveY() {
return removeY;
}
public void setY(float y) {
this.y = y;
}
public float getSpeedY() {
return speedY;
}
public void setSpeedY(float speedY) {
this.speedY = speedY;
}
public float getSpeedX() {
return speedX;
}
public void setSpeedX(float speedX) {
this.speedX = speedX;
}
public void setShaderColor(int color) {
originalColor = color;
this.shaderColor = new int[] {
ColorUtil.parseColor(color, "#cc"),
ColorUtil.parseColor(color, "#cc"),
ColorUtil.parseColor(color, "#cc"),
ColorUtil.parseColor(color, "#aa"),
ColorUtil.parseColor(color, "#99"),
ColorUtil.parseColor(color, "#77"),
ColorUtil.parseColor(color, "#44"),
ColorUtil.parseColor(color, "#00"),
};
}
public int[] getShaderColor() {
return this.shaderColor;
}
public int getOriginalColor() {
return originalColor;
}
}
####如何产生气泡呢? 气泡并不是一下子就出来, 而是隔一段时间产生一个, 而这个时间段又不是固定, 需要随机, 可以通过Handler 延迟产生气泡
/**
开始气泡上升动画
*/
@Override
public void startAnim() {
if (!mStarting) {
mStarting = true;
//此处开始需要通知不断绘制
invalidate();
//发送消息生成气泡
mHandler.sendEmptyMessage(MSG_CREATE_BUBBLE);
}
}
/**
停止气泡上升动画
*/
@Override
public void stopAnim() {
if (mStarting) {
mStarting = false;
mHandler.removeMessages(MSG_CREATE_BUBBLE);
mBubbles.clear();
invalidate();
}
}
@Override
public void invalidate() {
if (mStarting) {
super.invalidate();
}
}
private Handler mHandler = new Handler(secondLooper) {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_CREATE_BUBBLE) {
if (mStarting) {
if (mWidth > 0) {
Bubble bubble = new Bubble();
//此处随机气泡半径, 因项目控件width较小, 可自行设置气泡半径随机范围
int radius = mRandom.nextInt((int) (mWidth / 10f)) + (int)(mWidth / 7);
bubble.setRadius(radius);
bubble.setX(mWidth / 2);
bubble.setY(mHeight);
// - 0.5f 左右随机摆动
bubble.setSpeedX((mRandom.nextFloat() - 0.5f) * 2.3f);
bubble.setSpeedY(mRandom.nextFloat() * 10 + 1);
//此属性表示不一定所有气泡必须浮出水面才消失(可不要)
bubble.setRemoveY(mRandom.nextInt(radius));
//设置气泡颜色, 实际会渐变模糊
bubble.setShaderColor(ColorUtil.parseColor(mBubbleColors[mRandom.nextInt(mBubbleColors.length)], "#00"));
//将气泡添加到集合
mBubbles.add(bubble);
}
//下次产生气泡的时间
mHandler.sendEmptyMessageDelayed(MSG_CREATE_BUBBLE, (long) ((mRandom.nextInt(2) + 1) * 300));
}
}
}
};
List list = mBubbles;
for (Bubble bubble : list) {
float y = bubble.getY();
float x = bubble.getX();
float speedY = bubble.getSpeedY();
float speedX = bubble.getSpeedX();
float radius = bubble.getRadius();
if (y - bubble.getRemoveY() - speedY <= mTop) {
//消失后就移除
mBubbles.remove(bubble);
} else {
if ((x + speedX <= radius) || (x + speedX >= mWidth - radius)) {
//边缘处理 减缓左右摆动幅度
speedX = -speedX * 0.8f;
bubble.setSpeedX(speedX);
}
//变换x y坐标
x += speedX;
y -= speedY;
//减速 (加速减速可自行设置)
speedY *= 0.99f;
if (speedY < 1) {
//最小上升速度
speedY = 1;
}
bubble.setX(x);
bubble.setY(y);
bubble.setSpeedY(speedY);
//设置气泡渐变
RadialGradient shader = new RadialGradient(x, y, radius, bubble.getShaderColor(), null, Shader.TileMode.REPEAT);
mBubblePaint.setShader(shader);
canvas.drawCircle(x, y, radius, mBubblePaint);
//气泡碰撞计算
collision(bubble, x, y, radius);
}
}
private void collision(final Bubble bubble, final float x, final float y, final float radius) {
//开启线程避免界面卡顿
new Thread(new Runnable() {
@Override
public void run() {
for (Bubble tempBubble : mBubbles) {
if (tempBubble != bubble) {
float tempX = tempBubble.getX();
float tempY = tempBubble.getY();
float tempRadius = tempBubble.getRadius();
if (isCollisionWithCircle(x, y, tempX, tempY, radius, tempRadius)) {
//气泡的半径扩大
float r = Math.max(radius, tempRadius) * 1.015f;
float maxRadius = mWidth / 3f;
if (r > maxRadius){
r = maxRadius;
}
Bubble removeBubble, leaveBubble;
//移除下面气泡, 保留上面气泡
if (y < tempY) {
removeBubble = tempBubble;
leaveBubble = bubble;
} else {
removeBubble = bubble;
leaveBubble = tempBubble;
}
mBubbles.remove(removeBubble);
//模拟碰撞 方向改变
float tempLeaveSpeedX = leaveBubble.getSpeedX();
float leaveSpeedX = Math.abs(tempLeaveSpeedX);
if (leaveBubble.getX() < removeBubble.getX()) {
leaveSpeedX = -Math.abs(tempLeaveSpeedX);
}
//保留气泡属性设置
leaveBubble.setRadius(r);
leaveBubble.setSpeedX(leaveSpeedX * 0.9f);
//碰撞速度加快
leaveBubble.setSpeedY((removeBubble.getSpeedY() + leaveBubble.getSpeedY()) / 2f);
//leaveBubble.setShaderColor(blendColor(leaveBubble.getOriginalColor(), removeBubble.getOriginalColor()));
}
}
}
}
}).start();
}
/**
是否碰撞
*/
private boolean isCollisionWithCircle(float x1, float y1, float x2, float y2, float r1, float r2) {
//直角坐标系,依点1和点2做平行线,|x1-x2|为横向直角边,|y1-y2|为纵向直角边 依勾股定理 c^2=a^2+b^2
if (Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) <= (r1 + r2) * 0.85f) {
// 如果两圆的圆心距小于或等于两圆半径和则认为发生碰撞
return true;
}
return false;
}
/**
* Created by zengyan on 2017/3/10.
*/
public class ColorUtil {
/**
* 颜色设置alpha
* @param colorString 颜色值 如: "#ffffff"
* @param alphaString 透明度值 如: "#aa"
* @return
*/
public static int parseColor(String colorString, String alphaString) {
if (colorString.charAt(0) == '#') {
// Use a long to avoid rollovers on #ffXXXXXX
long color = Long.parseLong(colorString.substring(1), 16);
if (colorString.length() == 7) {
// Set the alpha value
// alpha |= 0x0000000000000000;
color = parseColor(color, alphaString);
} else if (colorString.length() != 9) {
throw new IllegalArgumentException("Unknown color");
}
return (int)color;
}
throw new IllegalArgumentException("Unknown color");
}
/**
* 颜色设置alpha
* @param color 颜色值
* @param alphaString 透明度值 如: "#aa"
* @return
*/
public static int parseColor(long color, String alphaString) {
if (alphaString.charAt(0) == '#' && alphaString.length() == 3) {
int alpha = Integer.parseInt(alphaString.substring(1), 16);
alpha = alpha << 24;
alpha &= 0x00000000ff000000;
color |= alpha;
} else {
throw new IllegalArgumentException("Unknown alpha");
}
return (int)color;
}
}