Android 气泡碰撞
最近有个需求要实现气泡碰撞的效果,本来有两个参考:
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 气泡碰撞相关推荐
- android 吐泡泡动画,android仿摩拜贴纸碰撞|气泡碰撞
转载请注明出处 准备 气泡碰撞最重要的就是边缘检测,气泡的运动涉及到重力,方向,重心,线速度,角速度,,等等一系列因素,想要在android 用view描述现实世界中的气泡实在是难度很大.网上查找资料 ...
- android仿摩拜贴纸碰撞|气泡碰撞
转载请注明出处 准备 气泡碰撞最重要的就是边缘检测,气泡的运动涉及到重力,方向,重心,线速度,角速度,等等一系列因素,想要在android 用view描述现实世界中的气泡实在是难度很大.网上查找资料后 ...
- Android 气泡动画(自定义View类)
Android 气泡动画(自定义View类) 一.前言 二.代码 1. 随机移动的气泡 2.热水气泡 一.前言 最近有需求制作一个水壶的气泡动画,首先在网上查找了一番,找到了一个文章. https:/ ...
- 01 C语言实现动态气泡碰撞和移动的效果,小球碰撞,Win7气泡壁纸,碰撞算法
C语言实现动态气泡碰撞和移动的效果 作者 将狼才鲸 创建日期 2023-01-29 Git源码仓库地址:C语言实现动态气泡碰撞和移动的效果 CSDN文章地址:01 C语言实现动态气泡碰撞和移动的效果 ...
- android气泡样式图片,Android实现三角形气泡效果方式汇总
在开发过程中,我们可能会经常遇到这样的需求样式: 这张图是截取京东消息通知的弹出框,我们可以看到右上方有个三角形的气泡效果,这只是其中一种,三角形的方向还可以是上.下.左.右. 通过截图可以发现,气泡 ...
- android 气泡.9,Android 三种方式实现三角形气泡效果、自定义View、shape、点9图
背景 这期需求中,项目需要这样一个情况,就是二级菜单上面有个三角形 乍一看,用个图片就可以解决,一个线性布局.垂直摆下去,所以一开始我是这样尝试的,后来发现美工给我切的图很不合适,不同手机显示效果也不 ...
- android气泡组件,Android 聊天气泡
网上搜到的只有一篇是自定义的TextView,其使用比较麻烦,所以采用大众化的方法--使用9.png来实现. 这里主要介绍sdk tool的draw9patch.bat的使用. 这个bat执行文件打开 ...
- Android气泡弹幕,Android弹幕实现:基于B站弹幕开源系统(7)QQ、微信聊天气泡样式的弹幕...
Android弹幕实现:基于B站弹幕开源系统(7)QQ.微信聊天气泡样式的弹幕 在附录文章得基础上,改进普通文本弹幕,实现一种特殊效果的文本弹幕,像QQ.微信一样的带有气泡背景的弹幕.实现的重点是在S ...
- Android气泡对话效果
需要的工具 下载对话气泡的PNG图片 android studio RecyclerView 时间和耐心 创建项目.制作Nine-Patch图片 Nine-Patch图片:一种被特殊处理过的png图片 ...
- android 气泡,Android Q 气泡
本节内容测试Android Q新功能 -- 气泡 当前文章首次编辑于Android Q Beta 2 版本,可能部分内容会在后期版本上发生变化,仅供参考 气泡是Android Q中的一项新功能.通过气 ...
最新文章
- GPS NMEA-0183协议常用数据格式及解析攻略
- AppWidget实现机制分析--launcher添加和删除appwidget深入分析
- 红外接收器c语言软件,红外线遥控解码接收程序_C语言.doc
- oracle 创建备份目录,Oracle rman创建和自动化备份
- Packet Tracer 思科模拟器入门教程 之一 初识Packet Tracer
- javah 找不到类文件的解决办法
- 【USB电压电流表】基于STM32F103C8T6 for Arduino
- Mybatis 批量更新运行异常,数据库 postgres
- java零基础学习第七天
- 雇佣兵的战斗力最大可以到达多少
- 系统变量和用户变量的区别
- git master、origin master 与 origin/master 的区别
- 程序员都秃顶?Python创始人笑了,养生还得学这门语言
- CodeMirror 基础配置指南
- Pynq_Z2-hdmi输出图像、文字流程(VDMA)
- VS打包项目exe文件
- 如何运用债市杠杆套利交易博取收益
- frame单选Java_Java Swing JRadioButton:单选按钮组件
- Rust入坑指南:有条不紊
- 如何做好水果店数据分析,水果店统计方法
热门文章
- 高德地图获取坐标距离_高德地图计算两坐标之间距离
- 如何查找各个手机放序列号
- 计算机高手如何操作键盘,从小白到高手 游戏键盘驱动全面解析
- 二维数组传参,用int指针接收
- Android 控制音频的音量大小
- python画一个正方形和圆_正方形最新:Python 用turtle实现用正方形画圆的例子_爱安网 LoveAn.com...
- 创业案例:如何调整股权,才不伤害合伙人感情?
- Vue 快速搭建页面模板
- Photoshop 有什么技巧让你相见恨晚?
- win7建行捷德U盾无法使用