几年前,看到过有个牛人用HTML5绘制了浪漫的爱心表白动画。地址在这:浪漫程序员 HTML5爱心表白动画。发现原来程序员也是可以很浪……漫…..的(PS:刚过520,被妹子骂不够浪漫)。那么在Android怎么打造如此这个效果呢?参考了一下前面HTML5的算法,在Android中实现了类似的效果。

生成心形线

心形线的表达式可以参考:桃心线。里面对桃心线的表达式解析的挺好。可以通过使用极坐标的方式,传入角度和距离(常量)计算出对应的坐标点。其中距离是常量值,不需改变,变化的是角度。

桃心线极坐标方程式为:x=16×sin 3 α

y=13×cosα−5×cos2α−2×cos3α−cos4α

如果生成的桃心线不够大,可以吧x、y乘以一个常数,使之变大。考虑到大部分人都不愿去研究具体的数学问题,我们直接把前面HTML5的JS代码直接翻译成Java代码就好。代码如下:public Point getHeartPoint(float angle) {  float t = (float) (angle / Math.PI);  float x = (float) (19.5 * (16 * Math.pow(Math.sin(t), 3)));  float y = (float) (-20 * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)));

return new Point(offsetX + (int) x, offsetY + (int) y);

}

其中offsetX和offsetY是偏移量。使用偏移量主要是为了能让心形线处于中央。offsetX和offsetY的值分别为:offsetX = width / 2;

offsetY = height / 2 - 55;

通过这个函数,我们可以将角度从(0,180)变化,不断取点并画点将这个心形线显示出来。好了,我们自定义一个View,然后把这个心形线画出来吧!@Override

protected void onDraw(Canvas canvas) {       float angle = 10;       while (angle

Point p = getHeartPoint(angle);

canvas.drawPoint(p.x, p.y, paint);

angle = angle + 0.02f;

}

}

绘制花瓣原理

我们想要的并不是简单绘制一个桃心线,要的是将花朵在桃心线上摆放。首先,得要知道怎么绘制花朵,而花朵是由一个个花瓣组成。因此绘制花朵的核心是绘制花瓣。绘制花瓣的原理是:3次贝塞尔曲线。三次贝塞尔曲线是由两个端点和两个控制点决定。假设花芯是一个圆,有n个花瓣,那么两个端点与花芯的圆心连线之间的夹角即为360/n。因此可以根据花瓣数量和花芯半径确定每个花瓣的位置。将两个端点与花芯的圆心连线的延长线分别确定另外两个控制点。通过随机生成花芯半径、每个花瓣的起始角以及随机确定延长线得到两个控制点,可以绘制一个随机的花朵。

将花朵绘制到桃心线上

一大波代码来袭

首先定义花瓣类Petal:package com.hc.testheart;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Path;/**

* Package com.example.administrator.testrecyclerview

* Created by HuaChao on 2016/5/25.

*/public class Petal {

private float stretchA;//第一个控制点延长线倍数

private float stretchB;//第二个控制点延长线倍数

private float startAngle;//起始旋转角,用于确定第一个端点

private float angle;//两条线之间夹角,由起始旋转角和夹角可以确定第二个端点

private int radius = 2;//花芯的半径

private float growFactor;//增长因子,花瓣是有开放的动画效果,这个参数决定花瓣展开速度

private int color;//花瓣颜色

private boolean isFinished = false;//花瓣是否绽放完成

private Path path = new Path();//用于保存三次贝塞尔曲线

private Paint paint = new Paint();//画笔

//构造函数,由花朵类调用

public Petal(float stretchA, float stretchB, float startAngle, float angle, int color, float growFactor) {        this.stretchA = stretchA;        this.stretchB = stretchB;        this.startAngle = startAngle;        this.angle = angle;        this.color = color;        this.growFactor = growFactor;

paint.setColor(color);

}    //用于渲染花瓣,通过不断更改半径使得花瓣越来越大

public void render(Point p, int radius, Canvas canvas) {        if (this.radius <= radius) {            this.radius += growFactor; // / 10;

} else {

isFinished = true;

}        this.draw(p, canvas);

}    //绘制花瓣,参数p是花芯的圆心的坐标

private void draw(Point p, Canvas canvas) {        if (!isFinished) {

path = new Path();            //将向量(0,radius)旋转起始角度,第一个控制点根据这个旋转后的向量计算

Point t = new Point(0, this.radius).rotate(MyUtil.degrad(this.startAngle));            //第一个端点,为了保证圆心不会随着radius增大而变大这里固定为3

Point v1 = new Point(0, 3).rotate(MyUtil.degrad(this.startAngle));            //第二个端点

Point v2 = t.clone().rotate(MyUtil.degrad(this.angle));            //延长线,分别确定两个控制点

Point v3 = t.clone().mult(this.stretchA);

Point v4 = v2.clone().mult(this.stretchB);            //由于圆心在p点,因此,每个点要加圆心坐标点

v1.add(p);

v2.add(p);

v3.add(p);

v4.add(p);

path.moveTo(v1.x, v1.y);            //参数分别是:第一个控制点,第二个控制点,终点

path.cubicTo(v3.x, v3.y, v4.x, v4.y, v2.x, v2.y);

}

canvas.drawPath(path, paint);

}

}

花瓣类是最重要的类,因为真正绘制在屏幕上的是一个个小花瓣。每个花朵包含一系列花瓣,花朵类Bloom如下:package com.hc.testheart;import android.graphics.Canvas;import java.util.ArrayList;/**

* Package com.example.administrator.testrecyclerview

* Created by HuaChao on 2016/5/25.

*/public class Bloom {

private int color;//整个花朵的颜色

private Point point;//花芯圆心

private int radius; //花芯半径

private ArrayList petals;//用于保存花瓣

public Point getPoint() {        return point;

}    public Bloom(Point point, int radius, int color, int petalCount) {        this.point = point;        this.radius = radius;        this.color = color;

petals = new ArrayList<>(petalCount);        float angle = 360f / petalCount;        int startAngle = MyUtil.randomInt(0, 90);        for (int i = 0; i

float stretchA = MyUtil.random(Garden.Options.minPetalStretch, Garden.Options.maxPetalStretch);            //随机产生第二个控制地的拉伸倍数

float stretchB = MyUtil.random(Garden.Options.minPetalStretch, Garden.Options.maxPetalStretch);            //计算每个花瓣的起始角度

int beginAngle = startAngle + (int) (i * angle);            //随机产生每个花瓣的增长因子(即绽放速度)

float growFactor = MyUtil.random(Garden.Options.minGrowFactor, Garden.Options.maxGrowFactor);            //创建一个花瓣,并添加到花瓣列表中

this.petals.add(new Petal(stretchA, stretchB, beginAngle, angle, color, growFactor));

}

}    //绘制花朵

public void draw(Canvas canvas) {

Petal p;        for (int i = 0; i

p = petals.get(i);            //渲染每朵花朵

p.render(point, this.radius, canvas);

}

}    public int getColor() {        return color;

}

}

接下来是花园类Garden,主要用于创建花朵以及一些相关配置:package com.hc.testheart;import java.util.ArrayList;/**

* Package com.example.administrator.testrecyclerview

* Created by HuaChao on 2016/5/24.

*/public class Garden {

//创建一个随机的花朵

public Bloom createRandomBloom(int x, int y) {        //创建一个随机的花朵半径

int radius = MyUtil.randomInt(Options.minBloomRadius, Options.maxBloomRadius);        //创建一个随机的花朵颜色

int color = MyUtil.randomrgba(Options.minRedColor, Options.maxRedColor, Options.minGreenColor, Options.maxGreenColor, Options.minBlueColor, Options.maxBlueColor, Options.opacity);        //创建随机的花朵中花瓣个数

int petalCount = MyUtil.randomInt(Options.minPetalCount, Options.maxPetalCount);        return createBloom(x, y, radius, color, petalCount);

}    //创建花朵

public Bloom createBloom(int x, int y, int radius, int color, int petalCount) {        return new Bloom(new Point(x, y), radius, color, petalCount);

}    static class Options {        //用于控制产生随机花瓣个数范围

public static int minPetalCount = 8;        public static int maxPetalCount = 15;        //用于控制产生延长线倍数范围

public static float minPetalStretch = 2f;        public static float maxPetalStretch = 3.5f;        //用于控制产生随机增长因子范围,增长因子决定花瓣绽放速度

public static float minGrowFactor = 1f;        public static float maxGrowFactor = 1.1f;        //用于控制产生花朵半径随机数范围

public static int minBloomRadius = 8;        public static int maxBloomRadius = 10;        //用于产生随机颜色

public static int minRedColor = 128;        public static int maxRedColor = 255;        public static int minGreenColor = 0;        public static int maxGreenColor = 128;        public static int minBlueColor = 0;        public static int maxBlueColor = 128;        //花瓣的透明度

public static int opacity = 50;//0.1

}

}

考虑到刷新的比较频繁,选择使用SurfaceView作为显示视图。自定义一个HeartView继承SurfaceView。代码如下:package com.hc.testheart;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.SurfaceHolder;import android.view.SurfaceView;import java.util.ArrayList;/**

* Package com.hc.testheart

* Created by HuaChao on 2016/5/25.

*/public class HeartView extends SurfaceView implements SurfaceHolder.Callback {

SurfaceHolder surfaceHolder;    int offsetX;    int offsetY;    private Garden garden;    private int width;    private int height;    private Paint backgroundPaint;    private boolean isDrawing = false;    private Bitmap bm;    private Canvas canvas;    private int heartRadio = 1;    public HeartView(Context context) {        super(context);

init();

}    public HeartView(Context context, AttributeSet attrs) {        super(context, attrs);

init();

}    private void init() {

surfaceHolder = getHolder();

surfaceHolder.addCallback(this);

garden = new Garden();

backgroundPaint = new Paint();

backgroundPaint.setColor(Color.rgb(0xff, 0xff, 0xe0));

}

ArrayList blooms = new ArrayList<>();    public Point getHeartPoint(float angle) {        float t = (float) (angle / Math.PI);        float x = (float) (heartRadio * (16 * Math.pow(Math.sin(t), 3)));        float y = (float) (-heartRadio * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)));        return new Point(offsetX + (int) x, offsetY + (int) y);

}    //绘制列表里所有的花朵

private void drawHeart() {

canvas.drawRect(0, 0, width, height, backgroundPaint);        for (Bloom b : blooms) {

b.draw(canvas);

}

Canvas c = surfaceHolder.lockCanvas();

c.drawBitmap(bm, 0, 0, null);

surfaceHolder.unlockCanvasAndPost(c);

}    public void reDraw() {

blooms.clear();

drawOnNewThread();

}    @Override

public void draw(Canvas canvas) {        super.draw(canvas);

}    //开启一个新线程绘制

private void drawOnNewThread() {        new Thread() {            @Override

public void run() {                if (isDrawing) return;

isDrawing = true;                float angle = 10;                while (true) {

Bloom bloom = getBloom(angle);                    if (bloom != null) {

blooms.add(bloom);

}                    if (angle >= 30) {                        break;

} else {

angle += 0.2;

}

drawHeart();                    try {

sleep(20);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

isDrawing = false;

}

}.start();

}    private Bloom getBloom(float angle) {

Point p = getHeartPoint(angle);        boolean draw = true;        /**循环比较新的坐标位置是否可以创建花朵,

* 为了防止花朵太密集

* */

for (int i = 0; i

Bloom b = blooms.get(i);

Point bp = b.getPoint();            float distance = (float) Math.sqrt(Math.pow(p.x - bp.x, 2) + Math.pow(p.y - bp.y, 2));            if (distance

draw = false;                break;

}

}        //如果位置间距满足要求,就在该位置创建花朵并将花朵放入列表

if (draw) {

Bloom bloom = garden.createRandomBloom(p.x, p.y);            return bloom;

}        return null;

}    @Override

public void surfaceCreated(SurfaceHolder holder) {

}    @Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {        this.width = width;        this.height = height;        //我的手机宽度像素是1080,发现参数设置为30比较合适,这里根据不同的宽度动态调整参数

heartRadio = width * 30 / 1080;

offsetX = width / 2;

offsetY = height / 2 - 55;

bm = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);

canvas = new Canvas(bm);

drawOnNewThread();

}    @Override

public void surfaceDestroyed(SurfaceHolder holder) {

}

}

还有两个比较重要的工具类

Point.java保存点信息,或者说是向量信息。包含向量的基本运算。package com.hc.testheart;/**

* Package com.hc.testheart

* Created by HuaChao on 2016/5/25.

*/public class Point {

public int x;    public int y;    public Point(int x, int y) {        this.x = x;        this.y = y;

}    //旋转

public Point rotate(float theta) {        int x = this.x;        int y = this.y;        this.x = (int) (Math.cos(theta) * x - Math.sin(theta) * y);        this.y = (int) (Math.sin(theta) * x + Math.cos(theta) * y);        return this;

}    //乘以一个常数

public Point mult(float f) {        this.x *= f;        this.y *= f;        return this;

}    //复制

public Point clone() {        return new Point(this.x, this.y);

}    //该点与圆心距离

public float length() {        return (float) Math.sqrt(this.x * this.x + this.y * this.y);

}    //向量相减

public Point subtract(Point p) {        this.x -= p.x;        this.y -= p.y;        return this;

}    //向量相加

public Point add(Point p) {        this.x += p.x;        this.y += p.y;        return this;

}    public Point set(int x, int y) {        this.x = x;        this.y = y;        return this;

}

}

工具类MyUtil.java主要是产生随机数、颜色等package com.hc.testheart;import android.graphics.Color;/**

* Package com.example.administrator.testrecyclerview

* Created by HuaChao on 2016/5/25.

*/public class MyUtil {

public static float circle = (float) (2 * Math.PI);    public static int rgba(int r, int g, int b, int a) {        return Color.argb(a, r, g, b);

}    public static int randomInt(int min, int max) {        return (int) Math.floor(Math.random() * (max - min + 1)) + min;

}    public static float random(float min, float max) {        return (float) (Math.random() * (max - min) + min);

}    //产生随机的argb颜色

public static int randomrgba(int rmin, int rmax, int gmin, int gmax, int bmin, int bmax, int a) {        int r = Math.round(random(rmin, rmax));        int g = Math.round(random(gmin, gmax));        int b = Math.round(random(bmin, bmax));        int limit = 5;        if (Math.abs(r - g) <= limit && Math.abs(g - b) <= limit && Math.abs(b - r) <= limit) {            return rgba(rmin, rmax, gmin, gmax);

} else {            return rgba(r, g, b, a);

}

}    //角度转弧度

public static float degrad(float angle) {        return circle / 360 * angle;

}

}

好了,目前为止,就可以得到上面的效果了。

android程序表白,打造一款浪漫的Android表白程序相关推荐

  1. 打造一款有趣的Android万能播放器之msysgit安装 github项目上传 修改 window中同步本地项目到github远程仓库

    项目初衷 由于所在的公司比较注重项目安全性,所以在项目同步这块几乎没有涉足,乘工作之余敲一个简单的app,起名为EasyLive,目的于熟悉一些工作之外的有趣知识点和巩固自己的基础知识. 项目内容 以 ...

  2. html表白earth,十句浪漫的英文表白句子

    如果你想让你的表白,你的情书更加动人,那么你不妨尝试一些使用英文句子,一些深情的英文句子,表达效果既可以避免中文的直白,还可以营造出不用的意味. 1.You are everything when y ...

  3. 手把手教你打造一款个人专属Android桌面

    实现方式两种 1.从头到尾写一个apk然后把系统的属性加上去,然后启动的时候默认就指定到这个apk的包名,他就启动, 2.我们基于Androidlauncher3的源码去做一个定制化的修改 分析一下这 ...

  4. php html word_手把手教你用PHP打造一款Word转HTML的程序

    [51CTO.com原创稿件]在一些特殊场景,用户需要展示的数据不是手动输入的纯文本或者手动编辑的富文本内容,而是通过上传Word文件形成生成***需要在APP上显示的HTML内容,也就是将上传的Wo ...

  5. 打造浪漫的Android表白程序

    几年前,看到过有个牛人用HTML5绘制了浪漫的爱心表白动画.地址在这:浪漫程序员 HTML5爱心表白动画.发现原来程序员也是可以很浪--漫-..的(PS:刚过520,被妹子骂不够浪漫).那么在Andr ...

  6. Unity安卓游戏开发:打造7款2D 3D游戏 Unity Android Game Development : Build 7 2D 3D Games

    流派:电子学习| MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz 语言:英语+中英文字幕(根据原英文字幕机译更准确) |大小:15.4 GB |时长:32h 55m Un ...

  7. 前端程序员的浪漫动态告白表白女友源码

    动态告白表白女友源码前端程序员的浪漫,是一款网页动态表白源码,直接点击html看效果,浏览器打开就好了,兄弟们可以上车了. 特别说明: 1.文字在 take.js 里面改 或者 index.html里 ...

  8. 浪漫的html表白源代码_Love:程序猿的方式【情人节amp;520—我爱你】动画加音效 → 那些年最浪漫的表白(帮你得到你的她)...

    Love:程序猿的方式~[情人节&520-我爱你]~动画加音效 → 那些年最浪漫的表白(帮你得到你的她) 导读 本文章代码的实现,主要参考GitHub(wuxia2001和hackerzhou ...

  9. 使用Python打造一款间谍程序

    知识点 这次我们使用python来打造一款间谍程序 程序中会用到许多知识点,大致分为四块 win32API  此处可以在MSDN上查看 Python基础重点在cpytes库的使用,使用方法请点击此处 ...

  10. 【程序员的浪漫】花式表白

    前言:520,七夕这些浪漫的节日你是否还在烦恼怎么过?表白不知如何下手?那就来看看这份浪漫的花式表白吧. 福利来了:下列分享表白六件套(5+1)的形式,表白的形式均以HTML实现,可以让没有编译器的女 ...

最新文章

  1. k近邻算法之 k值的选择
  2. 关于栈的理解(读书笔记)
  3. 云炬Qtpy5开发与实战笔记 3PyCharm添加PyUIC扩展——将.ui文件转换成.py文件
  4. 二十九、电子商务服务推荐项目基本描述
  5. SVG.js 基础图形绘制整理(二)
  6. mysql事务用法介绍及储存引擎介绍(MyLSAM,Innodb)
  7. 线性链表java实现_java实现线性单链表
  8. css 实现一个尖角_一个讲述了 CSS 相关的技巧、动画实现 的开源项目(60篇相关文章)...
  9. 什么样的前端框架才是一个好框架
  10. php mysql select where_PHP MySQL Where 子句
  11. java编程简单小游戏_求一个简单又有趣的JAVA小游戏代码
  12. 移动信号e经常无服务器,手机信号从4G变成E,是什么情况?移动客服作出解答...
  13. 2惠普暗影精灵恢复出厂设置
  14. vue 父传子 子传父实现方式
  15. [bzoj4816][Sdoi2017]数字表格 (反演+逆元)
  16. linux科学计算器_2个适用于Linux桌面的科学计算器
  17. DNS安全浅议、域名A记录(ANAME),MX记录,CNAME记录 专题
  18. matlab画动图留下末端点轨迹,请问Matlab robotic Toolbox中怎么让机械手运动时末端走过的轨迹显示出来?...
  19. Python源码学习笔记:Python程序执行过程与字节码
  20. eclipse的web工程中导入jir包如何放到lib目录中

热门文章

  1. 离散数学在计算机相关领域的应用
  2. 【140815】VC编程技巧280例 电子书下载
  3. 【编译原理】- 递归下降的语法分析器的实现
  4. 【计算机网络】信源编码——香农三大定理
  5. 笔记本电池续航测试软件,电池续航能力测试及整机试用总结
  6. 《烈烈先秦》6、六国的噩梦——大秦武安君白起
  7. Windows Server AppFabric简介
  8. win7鼠标指针主题包_MouseInc 小巧好用的鼠标手势软件
  9. Alcor(安国)AU6387量产修复(u盘修复)
  10. 编写一个生成器函数,能够生成斐波那契数列