# 打造浪漫的Android表白程序

## 生成心形线

x=16×sin3α
y=13×cosα?5×cos2α?2×cos3α?cos4α

 1 2 3 4 5 6 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);  }

 1 2  offsetX = width / 2;  offsetY = height / 2 - 55;

 1 2 3 4 5 6 7 8 9   @Override   protected void onDraw(Canvas canvas) {        float angle = 10;        while (angle < 180) {            Point p = getHeartPoint(angle);            canvas.drawPoint(p.x, p.y, paint);            angle = angle + 0.02f;         }    }

## 将花朵绘制到桃心线上

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72   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);     }     }   

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 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 < petalCount; 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 <  this.petals.size(); i++) {             p = petals.get(i);             //渲染每朵花朵             p.render(point, this.radius, canvas);           }       }       public int getColor() {         return color;     } }   

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 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     } }   

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 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 < blooms.size(); 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 < Garden.Options.maxBloomRadius * 1.5) {                 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保存点信息，或者说是向量信息。包含向量的基本运算。

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 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;     } }     

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 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表白秀恩爱源码

• 2015年01月26日 21:04
• 26.97MB
• 下载

## android表白经典源码

• 2014年03月26日 17:56
• 9.01MB
• 下载

## 程序员表白程序，开放源码，不断更新（第三篇：第二弹）

• wuxia2001
• 2015年05月18日 22:01
• 118214

## 打造浪漫的Android表白程序

• huachao1001
• 2016年05月25日 16:27
• 13774

## 程序员也是会浪漫的->打造浪漫的Android表白程序

• jim__charles
• 2016年11月27日 17:37
• 9817

## Android表白程序

• 2015年01月14日 19:43
• 9.01MB
• 下载

## 表白程序源码，android

android表白程序，有源码。欢迎下载
• a358763471
• 2014年08月22日 18:19
• 1677

## android表白APP源码，以程序员的姿势备战新年后的7夕

• 2016年01月22日 12:41
• 19.96MB
• 下载

## 程序员表白程序，开放源码，不断更新

• 2014年01月16日 18:10
• 9.01MB
• 下载

## 让C/C++程序员告诉你什么叫浪漫，表白黑科技，炫酷多彩求爱利器

• LxXlc468hW35lZn5
• 2017年12月03日 00:00
• 1465

举报原因： 您举报文章：打造浪漫的Android表白程序 色情 政治 抄袭 广告 招聘 骂人 其他 (最多只允许输入30个字)