Android 气泡上升效果

    xiaoxiao2021-03-25  83

    声明:


    根据原文 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<Bubble> 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);

    onDraw 所有气泡都在这里面绘制

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

    前方高能: 要绘制气泡了! drawBubble(Canvas canvas)

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

    代码基本上就是这么多了, 感谢原代码提供者, 希望对有需要的人有帮助, 后面有时间会研究气泡融合的效果, 目前思路为: 气泡碰撞时, 将要被融合的气泡放进 融合气泡内部list, 待canvas画融合气泡连同内部被融合的list一块画使用贝塞尔曲线达到融合效果.

    转载请注明原文地址: https://ju.6miu.com/read-19090.html

    最新回复(0)