Android特效专辑(八)——实现心型起泡飞舞的特效,让你的APP瞬间暖心


马上也要放年假了,家里估计会没网,更完这篇的话,可能要到年后了,不过在此期间会把更新内容都保存在本地,这样有网就可以发表了,也是极好的,今天说的这个特效,原本是Only上的一个小彩蛋的,我们来看看图片

Android特效专辑(八)——实现心型起泡飞舞的特效,让你的APP瞬间暖心 - 图1

只要我点击了Only这个字,下面就开始上升起起泡了,这个实现起来其实就是一个欲盖弥彰的动画而已,准备好三张颜色不一样的心型图片咯,这样的话,我们就开始动手来写一写吧!
首先新建一个工程——HeartFaom
准备工作就是准备图片咯

BezierEvaluator

  1. 单位转换以及计算轨迹
  1. package com.lgl.heartfaom;
  2. import android.animation.TypeEvaluator;
  3. import android.graphics.PointF;
  4. public class BezierEvaluator implements TypeEvaluator<PointF> {
  5. private PointF pointF1;
  6. private PointF pointF2;
  7. public BezierEvaluator(PointF pointF1, PointF pointF2) {
  8. this.pointF1 = pointF1;
  9. this.pointF2 = pointF2;
  10. }
  11. @Override
  12. public PointF evaluate(float time, PointF startValue, PointF endValue) {
  13. float timeLeft = 1.0f - time;
  14. PointF point = new PointF();// 结果
  15. point.x = timeLeft * timeLeft * timeLeft * (startValue.x) + 3
  16. * timeLeft * timeLeft * time * (pointF1.x) + 3 * timeLeft
  17. * time * time * (pointF2.x) + time * time * time * (endValue.x);
  18. point.y = timeLeft * timeLeft * timeLeft * (startValue.y) + 3
  19. * timeLeft * timeLeft * time * (pointF1.y) + 3 * timeLeft
  20. * time * time * (pointF2.y) + time * time * time * (endValue.y);
  21. return point;
  22. }
  23. }

PeriscopeLayout

  1. 贝塞尔曲线的计算以及气泡的实现
  1. package com.lgl.heartfaom;
  2. import java.util.Random;
  3. import android.animation.Animator;
  4. import android.animation.AnimatorListenerAdapter;
  5. import android.animation.AnimatorSet;
  6. import android.animation.ObjectAnimator;
  7. import android.animation.ValueAnimator;
  8. import android.annotation.TargetApi;
  9. import android.content.Context;
  10. import android.graphics.PointF;
  11. import android.graphics.drawable.Drawable;
  12. import android.os.Build;
  13. import android.util.AttributeSet;
  14. import android.view.View;
  15. import android.view.animation.AccelerateDecelerateInterpolator;
  16. import android.view.animation.AccelerateInterpolator;
  17. import android.view.animation.DecelerateInterpolator;
  18. import android.view.animation.Interpolator;
  19. import android.view.animation.LinearInterpolator;
  20. import android.widget.ImageView;
  21. import android.widget.RelativeLayout;
  22. public class PeriscopeLayout extends RelativeLayout {
  23. private Interpolator line = new LinearInterpolator();// 线性
  24. private Interpolator acc = new AccelerateInterpolator();// 加速
  25. private Interpolator dce = new DecelerateInterpolator();// 减速
  26. private Interpolator accdec = new AccelerateDecelerateInterpolator();// 先加速后减速
  27. private Interpolator[] interpolators;
  28. private int mHeight;
  29. private int mWidth;
  30. private LayoutParams lp;
  31. private Drawable[] drawables;
  32. private Random random = new Random();
  33. private int dHeight;
  34. private int dWidth;
  35. public PeriscopeLayout(Context context) {
  36. super(context);
  37. init();
  38. }
  39. public PeriscopeLayout(Context context, AttributeSet attrs) {
  40. super(context, attrs);
  41. init();
  42. }
  43. public PeriscopeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  44. super(context, attrs, defStyleAttr);
  45. init();
  46. }
  47. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  48. public PeriscopeLayout(Context context, AttributeSet attrs,
  49. int defStyleAttr, int defStyleRes) {
  50. super(context, attrs, defStyleAttr, defStyleRes);
  51. init();
  52. }
  53. private void init() {
  54. // 初始化显示的图片
  55. drawables = new Drawable[3];
  56. Drawable red = getResources().getDrawable(R.drawable.pl_red);
  57. Drawable yellow = getResources().getDrawable(R.drawable.pl_yellow);
  58. Drawable blue = getResources().getDrawable(R.drawable.pl_blue);
  59. drawables[0] = red;
  60. drawables[1] = yellow;
  61. drawables[2] = blue;
  62. // 获取图的宽高 用于后面的计算
  63. // 注意 我这里3张图片的大小都是一样的,所以我只取了一个
  64. dHeight = red.getIntrinsicHeight();
  65. dWidth = red.getIntrinsicWidth();
  66. // 底部 并且 水平居中
  67. lp = new LayoutParams(dWidth, dHeight);
  68. lp.addRule(CENTER_HORIZONTAL, TRUE);// 这里的TRUE 要注意 不是true
  69. lp.addRule(ALIGN_PARENT_BOTTOM, TRUE);
  70. // 初始化插补器
  71. interpolators = new Interpolator[4];
  72. interpolators[0] = line;
  73. interpolators[1] = acc;
  74. interpolators[2] = dce;
  75. interpolators[3] = accdec;
  76. }
  77. @Override
  78. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  79. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  80. mWidth = getMeasuredWidth();
  81. mHeight = getMeasuredHeight();
  82. }
  83. public void addHeart() {
  84. ImageView imageView = new ImageView(getContext());
  85. // 随机选一个
  86. imageView.setImageDrawable(drawables[random.nextInt(3)]);
  87. imageView.setLayoutParams(lp);
  88. addView(imageView);
  89. Animator set = getAnimator(imageView);
  90. set.addListener(new AnimEndListener(imageView));
  91. set.start();
  92. }
  93. private Animator getAnimator(View target) {
  94. AnimatorSet set = getEnterAnimtor(target);
  95. ValueAnimator bezierValueAnimator = getBezierValueAnimator(target);
  96. AnimatorSet finalSet = new AnimatorSet();
  97. finalSet.playSequentially(set);
  98. finalSet.playSequentially(set, bezierValueAnimator);
  99. finalSet.setInterpolator(interpolators[random.nextInt(4)]);
  100. finalSet.setTarget(target);
  101. return finalSet;
  102. }
  103. private AnimatorSet getEnterAnimtor(final View target) {
  104. ObjectAnimator alpha = ObjectAnimator.ofFloat(target, View.ALPHA, 0.2f,
  105. 1f);
  106. ObjectAnimator scaleX = ObjectAnimator.ofFloat(target, View.SCALE_X,
  107. 0.2f, 1f);
  108. ObjectAnimator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y,
  109. 0.2f, 1f);
  110. AnimatorSet enter = new AnimatorSet();
  111. enter.setDuration(500);
  112. enter.setInterpolator(new LinearInterpolator());
  113. enter.playTogether(alpha, scaleX, scaleY);
  114. enter.setTarget(target);
  115. return enter;
  116. }
  117. private ValueAnimator getBezierValueAnimator(View target) {
  118. // 初始化一个贝塞尔计算器- - 传入
  119. BezierEvaluator evaluator = new BezierEvaluator(getPointF(2),
  120. getPointF(1));
  121. // 这里最好画个图 理解一下 传入了起点 和 终点
  122. ValueAnimator animator = ValueAnimator.ofObject(evaluator, new PointF(
  123. (mWidth - dWidth) / 2, mHeight - dHeight),
  124. new PointF(random.nextInt(getWidth()), 0));
  125. animator.addUpdateListener(new BezierListenr(target));
  126. animator.setTarget(target);
  127. animator.setDuration(3000);
  128. return animator;
  129. }
  130. /**
  131. * 获取中间的两个 点
  132. *
  133. * @param scale
  134. */
  135. private PointF getPointF(int scale) {
  136. PointF pointF = new PointF();
  137. pointF.x = random.nextInt((mWidth - 100));// 减去100 是为了控制 x轴活动范围,看效果 随意~~
  138. // 再Y轴上 为了确保第二个点 在第一个点之上,我把Y分成了上下两半 这样动画效果好一些 也可以用其他方法
  139. pointF.y = random.nextInt((mHeight - 100)) / scale;
  140. return pointF;
  141. }
  142. private class BezierListenr implements ValueAnimator.AnimatorUpdateListener {
  143. private View target;
  144. public BezierListenr(View target) {
  145. this.target = target;
  146. }
  147. @Override
  148. public void onAnimationUpdate(ValueAnimator animation) {
  149. // 这里获取到贝塞尔曲线计算出来的的x y值 赋值给view 这样就能让爱心随着曲线走啦
  150. PointF pointF = (PointF) animation.getAnimatedValue();
  151. target.setX(pointF.x);
  152. target.setY(pointF.y);
  153. // 这里顺便做一个alpha动画
  154. target.setAlpha(1 - animation.getAnimatedFraction());
  155. }
  156. }
  157. private class AnimEndListener extends AnimatorListenerAdapter {
  158. private View target;
  159. public AnimEndListener(View target) {
  160. this.target = target;
  161. }
  162. @Override
  163. public void onAnimationEnd(Animator animation) {
  164. super.onAnimationEnd(animation);
  165. // 因为不停的add 导致子view数量只增不减,所以在view动画结束后remove掉
  166. removeView((target));
  167. }
  168. }
  169. }

activity_main.xml

  1. 布局的实现
  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. android:background="#000" >
  6. <Button
  7. android:id="@+id/btn_start"
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:layout_centerInParent="true"
  11. android:text="飞舞吧!" />
  12. <com.lgl.heartfaom.PeriscopeLayout
  13. android:id="@+id/periscope"
  14. android:layout_width="match_parent"
  15. android:layout_height="match_parent" >
  16. </com.lgl.heartfaom.PeriscopeLayout>
  17. </RelativeLayout>

MainActivity

  1. 接着就是怎么去使用它了
  1. package com.lgl.heartfaom;
  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. public class MainActivity extends Activity {
  8. private Button btn_start;
  9. // 心型气泡
  10. private PeriscopeLayout periscopeLayout;
  11. @Override
  12. protected void onCreate(Bundle savedInstanceState) {
  13. super.onCreate(savedInstanceState);
  14. setContentView(R.layout.activity_main);
  15. // 初始化
  16. periscopeLayout = (PeriscopeLayout) findViewById(R.id.periscope);
  17. btn_start = (Button) findViewById(R.id.btn_start);
  18. btn_start.setOnClickListener(new OnClickListener() {
  19. @Override
  20. public void onClick(View v) {
  21. // 调用添加泡泡的方法
  22. periscopeLayout.addHeart();
  23. }
  24. });
  25. }
  26. }

好,我们接下来就可以运行一下试试实际上的效果了

Android特效专辑(八)——实现心型起泡飞舞的特效,让你的APP瞬间暖心 - 图2

觉得不错的点个赞哦!

Demo下载地址:http://download.csdn.net/detail/qq_26787115/9422603