最近有个需求要实现气泡碰撞的效果,本来有两个参考:
1:仿摩拜贴纸动画的效果:

博客地址:http://m.blog.csdn.net/qq_28268507/article/details/74892372

采用jbox2d物理引擎来实现的,但是没法实现需求的效果

2:Android,气泡动画。(碰撞算法的半成品)

博客地址:http://blog.csdn.net/u010386612/article/details/50580080

博主采用动态绘制来实现,但是碰撞算法没有实现,这就很蛋疼了。

最后还是采用了第二种方式来实现,自己在博主的基础上改进代码,增加碰撞的算法,勉强实现了气泡碰撞动画。

效果如下:

由于图片现在,录制的不多,下面分析一下代码:

主要修改的地方有两点:

1:增加初始化圆球不重叠的逻辑

/*** 初始化圆球不重叠* @param mBall* @param position*/private void checkCollide(Ball mBall, int position) {float cx = mBall.cx;float cy = mBall.cy;int cr = mBall.radius;for (int i = 0; i < bList.size(); i++) {if (i == position) {return;}Ball balls = bList.get(i);if (checkCollideCircle(cx, cy, cr, balls.cx, balls.cy, balls.radius)) {mBalls[position].cx = mRandom.nextInt(mWidth - mBalls[i].radius) + mBalls[i].radius;mBalls[position].cy = mRandom.nextInt(mHeight - mBalls[i].radius) + mBalls[i].radius;checkCollide(mBalls[position], position);}}}/*** 两个圆的碰撞检测** @param x1* @param y1* @param r1* @param x2* @param y2* @param r2* @return*/private  boolean checkCollideCircle(float x1, float y1, int r1, float x2,float y2, int r2) {boolean iscollide = false;//两点之间的距离 小于两半径之和就发生了碰撞if (Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) <= r1 + r2+stoke) {iscollide = true;}else {iscollide = false;}return iscollide;}

由于圆心是随机的,初始化的时候尽量不要让圆球重叠,所以这里用了一个递归的算法,如果小球数量过多,会有内存问题。

2:增加气泡碰撞算法

 /*** 气泡碰撞算法* @param ball* @param position*/private void collisionDetectingAndChangeSpeeds(Ball ball, int position) {float cx = ball.cx;float cy = ball.cy;int cr = ball.radius;for (int i = 0; i < mCount; i++) {if (i == position) {return;}Ball balls = mBalls[i];if (checkCollideCircle(cx, cy, cr, balls.cx, balls.cy, balls.radius)) {ball.vx = -ball.vx;ball.vy = -ball.vy;balls.vx = -balls.vx;balls.vy = -balls.vy;}}}/*** 两个圆的碰撞检测** @param x1* @param y1* @param r1* @param x2* @param y2* @param r2* @return*/private  boolean checkCollideCircle(float x1, float y1, int r1, float x2,float y2, int r2) {boolean iscollide = false;//两点之间的距离 小于两半径之和就发生了碰撞if (Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) <= r1 + r2+stoke) {iscollide = true;}else {iscollide = false;}return iscollide;}

这里的气泡碰撞算法,看着其实很简单,考虑两两碰撞,不考虑更多的碰撞,因为太复杂,我的算法水平也是半吊子,别笑,主要的实现就是检测如果两个圆球距离小于等于半径,就速度反向。

整体实现的代码:

package com.orangetimes.filter.widget;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Looper;
import android.support.annotation.Nullable;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;import com.github.lzyzsd.randomcolor.RandomColor;
import com.orangetimes.filter.R;
import com.orangetimes.filter.model.TaskModel;import java.util.ArrayList;
import java.util.Random;/*** Created by zj on 2017/10/17.*/public class BallView extends View {private Random mRandom;private TextPaint tPaint;private ArrayList<TaskModel> datas = new ArrayList<>();class Ball {int radius; // 半径float cx;   // 圆心float cy;   // 圆心float vx; // X轴速度float vy; // Y轴速度Paint paint;String content;String taskId;// 移动void move() {//向角度的方向移动,偏移圆心cx += vx;cy += vy;}int left() {return (int) (cx - radius);}int right() {return (int) (cx + radius);}int bottom() {return (int) (cy + radius);}int top() {return (int) (cy - radius);}}private int mCount = 10;   // 小球个数private int maxRadius;  // 小球最大半径private int minRadius; // 小球最小半径private int minSpeed = 1; // 小球最小移动速度private int maxSpeed = 5; // 小球最大移动速度private int mWidth = 200;private int mHeight = 200;private int position;private RandomColor randomColor;private float stokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,getResources().getDimensionPixelSize(R.dimen.x5), getContext().getResources().getDisplayMetrics());private int stoke = getResources().getDimensionPixelSize(R.dimen.x5);public Ball[] mBalls;   // 用来保存所有小球的数组public ArrayList<Ball> bList;//保存设置圆心的小球集合public BallView(Context context) {this(context, null);}public BallView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initPaint();}private void initPaint() {bList = new ArrayList<>();// 初始化所有球(设置颜色和画笔, 初始化移动的角度)mRandom = new Random();randomColor = new RandomColor(); // 随机生成好看的颜色,github开源库。tPaint = new TextPaint();tPaint.setAntiAlias(true);tPaint.setTextAlign(Paint.Align.CENTER);tPaint.setColor(Color.WHITE);tPaint.setStyle(Paint.Style.FILL);tPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.x32));mBalls = new Ball[mCount];for (int i = 0; i < mCount; i++) {mBalls[i] = new Ball();// 设置画笔Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);paint.setColor(randomColor.randomColor());paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(stokeWidth);paint.setAlpha(180);// 设置速度float speedX = (mRandom.nextInt(maxSpeed - minSpeed + 1) + 5) / 10f;float speedY = (mRandom.nextInt(maxSpeed - minSpeed + 1) + 5) / 10f;mBalls[i].paint = paint;mBalls[i].vx = mRandom.nextBoolean() ? speedX : -speedX;mBalls[i].vy = mRandom.nextBoolean() ? speedY : -speedY;}}public BallView(Context context, AttributeSet attrs) {this(context, attrs, 0);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);mWidth = resolveSize(mWidth, widthMeasureSpec);mHeight = resolveSize(mHeight, heightMeasureSpec);setMeasuredDimension(mWidth, mHeight);maxRadius = mWidth / 10;minRadius = mWidth / 10;// 初始化圆的半径和圆心for (int i = 0; i < mCount; i++) {if (datas.size() > 0) {if ("1".equals(datas.get(i).getDifficultyLevel())) {mBalls[i].radius = mWidth / 14+stoke;} else if ("2".equals(datas.get(i).getDifficultyLevel())) {mBalls[i].radius = mWidth / 13+stoke;} else if ("3".equals(datas.get(i).getDifficultyLevel())) {mBalls[i].radius = mWidth / 12+stoke;} else if ("4".equals(datas.get(i).getDifficultyLevel())) {mBalls[i].radius = mWidth / 11+stoke;} else if ("5".equals(datas.get(i).getDifficultyLevel())) {mBalls[i].radius = maxRadius+stoke;} else {mBalls[i].radius = minRadius+stoke;}} else {mBalls[i].radius = maxRadius+stoke;}// 初始化圆心的位置, x最小为 radius, 最大为mwidth- radiusmBalls[i].cx = mRandom.nextInt(mWidth - mBalls[i].radius) + mBalls[i].radius;mBalls[i].cy = mRandom.nextInt(mHeight - mBalls[i].radius) + mBalls[i].radius;//bList.add(mBalls[i]);position = i;checkCollide(mBalls[position], position);}}/*** 初始化圆球不重叠* @param mBall* @param position*/private void checkCollide(Ball mBall, int position) {float cx = mBall.cx;float cy = mBall.cy;int cr = mBall.radius;for (int i = 0; i < bList.size(); i++) {if (i == position) {return;}Ball balls = bList.get(i);if (checkCollideCircle(cx, cy, cr, balls.cx, balls.cy, balls.radius)) {mBalls[position].cx = mRandom.nextInt(mWidth - mBalls[i].radius) + mBalls[i].radius;mBalls[position].cy = mRandom.nextInt(mHeight - mBalls[i].radius) + mBalls[i].radius;checkCollide(mBalls[position], position);}}}@Overrideprotected void onDraw(Canvas canvas) {long startTime = System.currentTimeMillis();/***  先画出所有圆*/if (datas.size() > 0) {for (int i = 0; i < datas.size(); i++) {Ball ball = mBalls[i];canvas.drawCircle(ball.cx, ball.cy, ball.radius, ball.paint);Rect rect = new Rect((int) (ball.cx - ball.radius), (int) (ball.cy - ball.radius), (int) (ball.cx + ball.radius), (int) (ball.cy + ball.radius));Paint.FontMetrics fontMetrics = tPaint.getFontMetrics();float top = fontMetrics.top;//为基线到字体上边框的距离,即上图中的topfloat bottom = fontMetrics.bottom;//为基线到字体下边框的距离,即上图中的bottomint baseLineY = (int) (rect.centerY() + top);//基线中间点的y轴计算公式String str = datas.get(i).getName();ball.content = datas.get(i).getName();ball.taskId = datas.get(i).getId();StaticLayout layout = new StaticLayout(str, tPaint, 2 * ball.radius, Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, true);canvas.save();canvas.translate(rect.centerX(), baseLineY);//从100,100开始画layout.draw(canvas);canvas.restore();//别忘了restore
//            canvas.drawText(str, rect.centerX(), baseLineY, tPaint);}/***  球碰撞边界*/for (int i = 0; i < datas.size(); i++) {Ball ball = mBalls[i];collisionDetectingAndChangeSpeed(ball); // 碰撞边界的计算collisionDetectingAndChangeSpeeds(ball, i);ball.move(); // 移动}long stopTime = System.currentTimeMillis();long runTime = stopTime - startTime;// 16毫秒执行一次postInvalidateDelayed(Math.abs(runTime - 16));}}public void setData(ArrayList<TaskModel> mList) {this.mCount = mList.size();this.datas = mList;if (Looper.getMainLooper() == Looper.myLooper()) {invalidate();} else {postInvalidate();}}/*** 气泡碰撞算法* @param ball* @param position*/private void collisionDetectingAndChangeSpeeds(Ball ball, int position) {float cx = ball.cx;float cy = ball.cy;int cr = ball.radius;for (int i = 0; i < mCount; i++) {if (i == position) {return;}Ball balls = mBalls[i];if (checkCollideCircle(cx, cy, cr, balls.cx, balls.cy, balls.radius)) {ball.vx = -ball.vx;ball.vy = -ball.vy;balls.vx = -balls.vx;balls.vy = -balls.vy;}}}/*** 两个圆的碰撞检测** @param x1* @param y1* @param r1* @param x2* @param y2* @param r2* @return*/private  boolean checkCollideCircle(float x1, float y1, int r1, float x2,float y2, int r2) {boolean iscollide = false;//两点之间的距离 小于两半径之和就发生了碰撞if (Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) <= r1 + r2+stoke) {iscollide = true;}else {iscollide = false;}return iscollide;}// 判断球是否碰撞碰撞边界public void collisionDetectingAndChangeSpeed(Ball ball) {int left = getLeft();int top = getTop();int right = getRight();int bottom = getBottom();float speedX = ball.vx;float speedY = ball.vy;// 碰撞左右,X的速度取反。 speed的判断是防止重复检测碰撞,然后黏在墙上了=。=if (ball.left() <= left && speedX < 0) {ball.vx = -ball.vx;} else if (ball.top() <= top && speedY < 0) {ball.vy = -ball.vy;} else if (ball.right() >= right && speedX > 0) {ball.vx = -ball.vx;} else if (ball.bottom() >= bottom && speedY > 0) {ball.vy = -ball.vy;}}float x, y;@Overridepublic boolean onTouchEvent(MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_DOWN) {x = event.getX();y = event.getY();for (int i = 0; i < mCount; i++) {Ball ball = mBalls[i];//点击位置x坐标与圆心的x坐标的距离int distanceX = (int) Math.abs(ball.cx - x);//点击位置y坐标与圆心的y坐标的距离int distanceY = (int) Math.abs(ball.cy - y);//点击位置与圆心的直线距离int distanceZ = (int) Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2));if (distanceZ <= ball.radius) {Log.e("ball---->", ball.content);progressListener.getTaskId(ball.taskId);}}}return true;}public interface BallViewListener {void getTaskId(String taskId);}public BallViewListener progressListener;public void setBallViewListener(BallViewListener listener) {progressListener = listener;}
}

这里面需要提到的就是由于是动态刷新,所以在加载数据的时候需要调用requestLayout,

开放的接口如下:

  if (list != null) {ballview.setData(list);ballview.requestLayout();}

效果:

Android 气泡碰撞相关推荐

  1. android 吐泡泡动画,android仿摩拜贴纸碰撞|气泡碰撞

    转载请注明出处 准备 气泡碰撞最重要的就是边缘检测,气泡的运动涉及到重力,方向,重心,线速度,角速度,,等等一系列因素,想要在android 用view描述现实世界中的气泡实在是难度很大.网上查找资料 ...

  2. android仿摩拜贴纸碰撞|气泡碰撞

    转载请注明出处 准备 气泡碰撞最重要的就是边缘检测,气泡的运动涉及到重力,方向,重心,线速度,角速度,等等一系列因素,想要在android 用view描述现实世界中的气泡实在是难度很大.网上查找资料后 ...

  3. Android 气泡动画(自定义View类)

    Android 气泡动画(自定义View类) 一.前言 二.代码 1. 随机移动的气泡 2.热水气泡 一.前言 最近有需求制作一个水壶的气泡动画,首先在网上查找了一番,找到了一个文章. https:/ ...

  4. 01 C语言实现动态气泡碰撞和移动的效果,小球碰撞,Win7气泡壁纸,碰撞算法

    C语言实现动态气泡碰撞和移动的效果 作者 将狼才鲸 创建日期 2023-01-29 Git源码仓库地址:C语言实现动态气泡碰撞和移动的效果 CSDN文章地址:01 C语言实现动态气泡碰撞和移动的效果 ...

  5. android气泡样式图片,Android实现三角形气泡效果方式汇总

    在开发过程中,我们可能会经常遇到这样的需求样式: 这张图是截取京东消息通知的弹出框,我们可以看到右上方有个三角形的气泡效果,这只是其中一种,三角形的方向还可以是上.下.左.右. 通过截图可以发现,气泡 ...

  6. android 气泡.9,Android 三种方式实现三角形气泡效果、自定义View、shape、点9图

    背景 这期需求中,项目需要这样一个情况,就是二级菜单上面有个三角形 乍一看,用个图片就可以解决,一个线性布局.垂直摆下去,所以一开始我是这样尝试的,后来发现美工给我切的图很不合适,不同手机显示效果也不 ...

  7. android气泡组件,Android 聊天气泡

    网上搜到的只有一篇是自定义的TextView,其使用比较麻烦,所以采用大众化的方法--使用9.png来实现. 这里主要介绍sdk tool的draw9patch.bat的使用. 这个bat执行文件打开 ...

  8. Android气泡弹幕,Android弹幕实现:基于B站弹幕开源系统(7)QQ、微信聊天气泡样式的弹幕...

    Android弹幕实现:基于B站弹幕开源系统(7)QQ.微信聊天气泡样式的弹幕 在附录文章得基础上,改进普通文本弹幕,实现一种特殊效果的文本弹幕,像QQ.微信一样的带有气泡背景的弹幕.实现的重点是在S ...

  9. Android气泡对话效果

    需要的工具 下载对话气泡的PNG图片 android studio RecyclerView 时间和耐心 创建项目.制作Nine-Patch图片 Nine-Patch图片:一种被特殊处理过的png图片 ...

  10. android 气泡,Android Q 气泡

    本节内容测试Android Q新功能 -- 气泡 当前文章首次编辑于Android Q Beta 2 版本,可能部分内容会在后期版本上发生变化,仅供参考 气泡是Android Q中的一项新功能.通过气 ...

最新文章

  1. GPS NMEA-0183协议常用数据格式及解析攻略
  2. AppWidget实现机制分析--launcher添加和删除appwidget深入分析
  3. 红外接收器c语言软件,红外线遥控解码接收程序_C语言.doc
  4. oracle 创建备份目录,Oracle rman创建和自动化备份
  5. Packet Tracer 思科模拟器入门教程 之一 初识Packet Tracer
  6. javah 找不到类文件的解决办法
  7. 【USB电压电流表】基于STM32F103C8T6 for Arduino
  8. Mybatis 批量更新运行异常,数据库 postgres
  9. java零基础学习第七天
  10. 雇佣兵的战斗力最大可以到达多少
  11. 系统变量和用户变量的区别
  12. git master、origin master 与 origin/master 的区别
  13. 程序员都秃顶?Python创始人笑了,养生还得学这门语言
  14. CodeMirror 基础配置指南
  15. Pynq_Z2-hdmi输出图像、文字流程(VDMA)
  16. VS打包项目exe文件
  17. 如何运用债市杠杆套利交易博取收益
  18. frame单选Java_Java Swing JRadioButton:单选按钮组件
  19. Rust入坑指南:有条不紊
  20. 如何做好水果店数据分析,水果店统计方法

热门文章

  1. 高德地图获取坐标距离_高德地图计算两坐标之间距离
  2. 如何查找各个手机放序列号
  3. 计算机高手如何操作键盘,从小白到高手 游戏键盘驱动全面解析
  4. 二维数组传参,用int指针接收
  5. Android 控制音频的音量大小
  6. python画一个正方形和圆_正方形最新:Python 用turtle实现用正方形画圆的例子_爱安网 LoveAn.com...
  7. 创业案例:如何调整股权,才不伤害合伙人感情?
  8. Vue 快速搭建页面模板
  9. Photoshop 有什么技巧让你相见恨晚?
  10. win7建行捷德U盾无法使用