Android特效专辑(六)——仿QQ聊天撒花特效,无形装逼,最为致命

我的关于特效的专辑已经在CSDN上申请了一个专栏——http://blog.csdn.net/column/details/liuguilin.html
日后我所写的特效专辑也会以一添加在这个专栏上,今天写的这个特效,是关于聊天的,你肯定遇到过,就是你跟人家聊天的时候,比如发送应(么么哒),然后屏幕上全部就是表情了,今天我们就是做这个,撒花的特效,国际惯例,上图

截图

  1. 实现这样的效果,你要知道贝塞尔曲线,何谓贝塞尔曲线?其实就是曲线,嘿嘿,关于曲线的概念大家可以去

Android绘图机制(二)——自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解

中看下,我们这里就直接写了

1.activity_main.xml

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent" >
  5. //撒花的区域
  6. <RelativeLayout
  7. android:id="@+id/rlt_animation_layout"
  8. android:layout_width="match_parent"
  9. android:layout_height="match_parent" >
  10. </RelativeLayout>
  11. <Button
  12. android:id="@+id/btn_start"
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:layout_alignParentBottom="true"
  16. android:layout_centerHorizontal="true"
  17. android:layout_marginBottom="23dp"
  18. android:text="开始撒花" />
  19. </RelativeLayout>

2.Fllower

  1. 传参类
  1. package com.lgl.test;
  2. import android.graphics.Bitmap;
  3. import android.graphics.Path;
  4. import java.io.Serializable;
  5. public class Fllower implements Serializable {
  6. private static final long serialVersionUID = 1L;
  7. private Bitmap image;
  8. private float x;
  9. private float y;
  10. private Path path;
  11. private float value;
  12. public Bitmap getResId() {
  13. return image;
  14. }
  15. public void setResId(Bitmap img) {
  16. this.image = img;
  17. }
  18. public float getX() {
  19. return x;
  20. }
  21. public void setX(float x) {
  22. this.x = x;
  23. }
  24. public float getY() {
  25. return y;
  26. }
  27. public void setY(float y) {
  28. this.y = y;
  29. }
  30. public Path getPath() {
  31. return path;
  32. }
  33. public void setPath(Path path) {
  34. this.path = path;
  35. }
  36. public float getValue() {
  37. return value;
  38. }
  39. public void setValue(float value) {
  40. this.value = value;
  41. }
  42. @Override
  43. public String toString() {
  44. return "Fllower [ x=" + x + ", y=" + y + ", path=" + path + ", value="
  45. + value + "]";
  46. }
  47. }

3.FllowerAnimation

  1. 动画类
  1. package com.lgl.test;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.Random;
  5. import android.animation.ObjectAnimator;
  6. import android.animation.ValueAnimator;
  7. import android.animation.ValueAnimator.AnimatorUpdateListener;
  8. import android.content.Context;
  9. import android.graphics.Bitmap;
  10. import android.graphics.BitmapFactory;
  11. import android.graphics.Canvas;
  12. import android.graphics.Paint;
  13. import android.graphics.Path;
  14. import android.graphics.PathMeasure;
  15. import android.util.Log;
  16. import android.util.TypedValue;
  17. import android.view.View;
  18. import android.view.WindowManager;
  19. import android.view.animation.AccelerateInterpolator;
  20. /**
  21. * 撒花 用到的知识点: 1、android属性动画 2、Path路径绘制 3、贝塞尔曲线
  22. */
  23. public class FllowerAnimation extends View implements AnimatorUpdateListener {
  24. /**
  25. * 动画改变的属性值
  26. */
  27. private float phase1 = 0f;
  28. private float phase2 = 0f;
  29. private float phase3 = 0f;
  30. /**
  31. * 小球集合
  32. */
  33. private List<Fllower> fllowers1 = new ArrayList<Fllower>();
  34. private List<Fllower> fllowers2 = new ArrayList<Fllower>();
  35. private List<Fllower> fllowers3 = new ArrayList<Fllower>();
  36. /**
  37. * 动画播放的时间
  38. */
  39. private int time = 4000;
  40. /**
  41. * 动画间隔
  42. */
  43. private int delay = 400;
  44. int[] ylocations = { -100, -50, -25, 0 };
  45. /**
  46. * 资源ID
  47. */
  48. // private int resId = R.drawable.fllower_love;
  49. public FllowerAnimation(Context context) {
  50. super(context);
  51. init(context);
  52. // this.resId = resId;
  53. }
  54. @SuppressWarnings("deprecation")
  55. private void init(Context context) {
  56. WindowManager wm = (WindowManager) context
  57. .getSystemService(Context.WINDOW_SERVICE);
  58. width = wm.getDefaultDisplay().getWidth();
  59. height = (int) (wm.getDefaultDisplay().getHeight() * 3 / 2f);
  60. mPaint = new Paint();
  61. mPaint.setAntiAlias(true);
  62. // mPaint.setStrokeWidth(2);
  63. // mPaint.setColor(Color.BLUE);
  64. // mPaint.setStyle(Style.STROKE);
  65. pathMeasure = new PathMeasure();
  66. builderFollower(fllowerCount, fllowers1);
  67. builderFollower(fllowerCount, fllowers2);
  68. builderFollower(fllowerCount, fllowers3);
  69. }
  70. /**
  71. * 宽度
  72. */
  73. private int width = 0;
  74. /**
  75. * 高度
  76. */
  77. private int height = 0;
  78. /**
  79. * 曲线高度个数分割
  80. */
  81. private int quadCount = 10;
  82. /**
  83. * 曲度
  84. */
  85. private float intensity = 0.2f;
  86. /**
  87. * 第一批个数
  88. */
  89. private int fllowerCount = 4;
  90. /**
  91. * 创建花
  92. */
  93. private void builderFollower(int count, List<Fllower> fllowers) {
  94. int max = (int) (width * 3 / 4f);
  95. int min = (int) (width / 4f);
  96. Random random = new Random();
  97. for (int i = 0; i < count; i++) {
  98. int s = random.nextInt(max) % (max - min + 1) + min;
  99. Path path = new Path();
  100. CPoint CPoint = new CPoint(s, ylocations[random.nextInt(3)]);
  101. List<CPoint> points = builderPath(CPoint);
  102. drawFllowerPath(path, points);
  103. Fllower fllower = new Fllower();
  104. fllower.setPath(path);
  105. Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
  106. R.drawable.lift_flower);
  107. fllower.setResId(bitmap);
  108. fllowers.add(fllower);
  109. }
  110. }
  111. /**
  112. * 画曲线
  113. *
  114. * @param path
  115. * @param points
  116. */
  117. private void drawFllowerPath(Path path, List<CPoint> points) {
  118. if (points.size() > 1) {
  119. for (int j = 0; j < points.size(); j++) {
  120. CPoint point = points.get(j);
  121. if (j == 0) {
  122. CPoint next = points.get(j + 1);
  123. point.dx = ((next.x - point.x) * intensity);
  124. point.dy = ((next.y - point.y) * intensity);
  125. } else if (j == points.size() - 1) {
  126. CPoint prev = points.get(j - 1);
  127. point.dx = ((point.x - prev.x) * intensity);
  128. point.dy = ((point.y - prev.y) * intensity);
  129. } else {
  130. CPoint next = points.get(j + 1);
  131. CPoint prev = points.get(j - 1);
  132. point.dx = ((next.x - prev.x) * intensity);
  133. point.dy = ((next.y - prev.y) * intensity);
  134. }
  135. // create the cubic-spline path
  136. if (j == 0) {
  137. path.moveTo(point.x, point.y);
  138. } else {
  139. CPoint prev = points.get(j - 1);
  140. path.cubicTo(prev.x + prev.dx, (prev.y + prev.dy), point.x
  141. - point.dx, (point.y - point.dy), point.x, point.y);
  142. }
  143. }
  144. }
  145. }
  146. /**
  147. * 曲线摇摆的幅度
  148. */
  149. private int range = (int) TypedValue
  150. .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 70, getResources()
  151. .getDisplayMetrics());
  152. /**
  153. * 画路径
  154. *
  155. * @param point
  156. * @return
  157. */
  158. private List<CPoint> builderPath(CPoint point) {
  159. List<CPoint> points = new ArrayList<CPoint>();
  160. Random random = new Random();
  161. for (int i = 0; i < quadCount; i++) {
  162. if (i == 0) {
  163. points.add(point);
  164. } else {
  165. CPoint tmp = new CPoint(0, 0);
  166. if (random.nextInt(100) % 2 == 0) {
  167. tmp.x = point.x + random.nextInt(range);
  168. } else {
  169. tmp.x = point.x - random.nextInt(range);
  170. }
  171. tmp.y = (int) (height / (float) quadCount * i);
  172. points.add(tmp);
  173. }
  174. }
  175. return points;
  176. }
  177. /**
  178. * 画笔
  179. */
  180. private Paint mPaint;
  181. /**
  182. * 测量路径的坐标位置
  183. */
  184. private PathMeasure pathMeasure = null;
  185. @Override
  186. protected void onDraw(Canvas canvas) {
  187. super.onDraw(canvas);
  188. drawFllower(canvas, fllowers1);
  189. drawFllower(canvas, fllowers2);
  190. drawFllower(canvas, fllowers3);
  191. }
  192. /**
  193. * 高度往上偏移量,把开始点移出屏幕顶部
  194. */
  195. private float dy = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
  196. 40, getResources().getDisplayMetrics());
  197. /**
  198. * @param canvas
  199. * @param fllowers
  200. */
  201. private void drawFllower(Canvas canvas, List<Fllower> fllowers) {
  202. for (Fllower fllower : fllowers) {
  203. float[] pos = new float[2];
  204. // canvas.drawPath(fllower.getPath(),mPaint);
  205. pathMeasure.setPath(fllower.getPath(), false);
  206. pathMeasure.getPosTan(height * fllower.getValue(), pos, null);
  207. // canvas.drawCircle(pos[0], pos[1], 10, mPaint);
  208. canvas.drawBitmap(fllower.getResId(), pos[0], pos[1] - dy, null);
  209. }
  210. }
  211. ObjectAnimator mAnimator1;
  212. ObjectAnimator mAnimator2;
  213. ObjectAnimator mAnimator3;
  214. public void startAnimation() {
  215. if (mAnimator1 != null && mAnimator1.isRunning()) {
  216. mAnimator1.cancel();
  217. }
  218. mAnimator1 = ObjectAnimator.ofFloat(this, "phase1", 0f, 1f);
  219. mAnimator1.setDuration(time);
  220. mAnimator1.addUpdateListener(this);
  221. mAnimator1.start();
  222. mAnimator1.setInterpolator(new AccelerateInterpolator(1f));
  223. if (mAnimator2 != null && mAnimator2.isRunning()) {
  224. mAnimator2.cancel();
  225. }
  226. mAnimator2 = ObjectAnimator.ofFloat(this, "phase2", 0f, 1f);
  227. mAnimator2.setDuration(time);
  228. mAnimator2.addUpdateListener(this);
  229. mAnimator2.start();
  230. mAnimator2.setInterpolator(new AccelerateInterpolator(1f));
  231. mAnimator2.setStartDelay(delay);
  232. if (mAnimator3 != null && mAnimator3.isRunning()) {
  233. mAnimator3.cancel();
  234. }
  235. mAnimator3 = ObjectAnimator.ofFloat(this, "phase3", 0f, 1f);
  236. mAnimator3.setDuration(time);
  237. mAnimator3.addUpdateListener(this);
  238. mAnimator3.start();
  239. mAnimator3.setInterpolator(new AccelerateInterpolator(1f));
  240. mAnimator3.setStartDelay(delay * 2);
  241. }
  242. /**
  243. * 跟新小球的位置
  244. *
  245. * @param value
  246. * @param fllowers
  247. */
  248. private void updateValue(float value, List<Fllower> fllowers) {
  249. for (Fllower fllower : fllowers) {
  250. fllower.setValue(value);
  251. }
  252. }
  253. /**
  254. * 动画改变回调
  255. */
  256. @Override
  257. public void onAnimationUpdate(ValueAnimator arg0) {
  258. updateValue(getPhase1(), fllowers1);
  259. updateValue(getPhase2(), fllowers2);
  260. updateValue(getPhase3(), fllowers3);
  261. Log.i(tag, getPhase1() + "");
  262. invalidate();
  263. }
  264. public float getPhase1() {
  265. return phase1;
  266. }
  267. public void setPhase1(float phase1) {
  268. this.phase1 = phase1;
  269. }
  270. public float getPhase2() {
  271. return phase2;
  272. }
  273. public void setPhase2(float phase2) {
  274. this.phase2 = phase2;
  275. }
  276. public float getPhase3() {
  277. return phase3;
  278. }
  279. public void setPhase3(float phase3) {
  280. this.phase3 = phase3;
  281. }
  282. private String tag = this.getClass().getSimpleName();
  283. private class CPoint {
  284. public float x = 0f;
  285. public float y = 0f;
  286. /**
  287. * x-axis distance
  288. */
  289. public float dx = 0f;
  290. /**
  291. * y-axis distance
  292. */
  293. public float dy = 0f;
  294. public CPoint(float x, float y) {
  295. this.x = x;
  296. this.y = y;
  297. }
  298. }
  299. }

4.MainActivity

  1. 接着就看我们使用
  1. package com.lgl.test;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.View;
  5. import android.view.View.OnClickListener;
  6. import android.widget.Button;
  7. import android.widget.RelativeLayout;
  8. public class MainActivity extends Activity {
  9. private Button btn_start;
  10. // 撒花特效
  11. private RelativeLayout rlt_animation_layout;
  12. private FllowerAnimation fllowerAnimation;
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.activity_main);
  17. // 撒花初始化
  18. rlt_animation_layout = (RelativeLayout) findViewById(R.id.rlt_animation_layout);
  19. rlt_animation_layout.setVisibility(View.VISIBLE);
  20. fllowerAnimation = new FllowerAnimation(this);
  21. RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
  22. RelativeLayout.LayoutParams.MATCH_PARENT,
  23. RelativeLayout.LayoutParams.MATCH_PARENT);
  24. fllowerAnimation.setLayoutParams(params);
  25. rlt_animation_layout.addView(fllowerAnimation);
  26. btn_start = (Button) findViewById(R.id.btn_start);
  27. btn_start.setOnClickListener(new OnClickListener() {
  28. @Override
  29. public void onClick(View v) {
  30. // 开始撒花
  31. fllowerAnimation.startAnimation();
  32. }
  33. });
  34. }
  35. }
  1. 好,我们现在来看看效果
  1. 好的,你也赶快去试一下吧!

Demo下载:http://download.csdn.net/detail/qq_26787115/9410578