之前看了一款有点黄的17app底角的爱心各种乱飞,好奇这种效果的实现方式,恰巧看到这篇文章:程序亦非猿:一步一步教你实现Periscope点赞效果,遂按照其思路实现了一个落叶飘零的效果,如下动图:

实现的要点如下:

  1. 值动画的使用
  2. 贝塞尔公式估值器的设置
  3. 落叶的起点、途径点、终点处理
  4. Activity退出时动画和子线程的处理,防止内存泄露

实现步骤:

① 控件初始化添加叶子集合和补间器集合

    public FloatLeafLayout(Context context, AttributeSet attrs) {super(context, attrs);init();}private void init() {// 四张不同形状的叶子mLeafs = new Drawable[]{getResources().getDrawable(R.mipmap.leaf_1),getResources().getDrawable(R.mipmap.leaf_2),getResources().getDrawable(R.mipmap.leaf_3),getResources().getDrawable(R.mipmap.leaf_4)};// 四个不同的补间器mInterpolator = new Interpolator[]{new AccelerateDecelerateInterpolator(),new AccelerateInterpolator(),new DecelerateInterpolator(),new LinearInterpolator()};}

② onMeasure()测出宽高,并且添加树,树的图片做了缩放处理

    @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);setMeasuredDimension(mWidthSize = measure(widthMeasureSpec), mHeightSize = measure(heightMeasureSpec));if (getChildCount() == 0) {addTree(mWidthSize, mHeightSize);}}private int measure(int measureSpec) {int result = 0;int mode = MeasureSpec.getMode(measureSpec);int size = MeasureSpec.getSize(measureSpec);if (mode == MeasureSpec.EXACTLY) {result = size;} else {result = dip2px(getContext(), 300);if (mode == MeasureSpec.AT_MOST) {result = Math.min(result, size);}}return result;}// 添加树的图片private void addTree(int reqWidth, int reqHeight) {BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeResource(getResources(), R.mipmap.tree, options);final int outWidth = options.outWidth;final int outHeight = options.outHeight;int inSampleSize = 1;if (outWidth > reqWidth || outHeight > reqHeight) {final int widthRatio = outWidth / reqWidth;final int heightRatio = outHeight / reqHeight;inSampleSize = Math.min(widthRatio, heightRatio);}options.inSampleSize = inSampleSize == 0 ? 1 : inSampleSize;options.inJustDecodeBounds = false;ImageView mTree = new ImageView(getContext());final Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.tree, options);mTree.setBackgroundDrawable(new BitmapDrawable(bitmap));addView(mTree, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);}

③ 接下来就是暴露添加树叶:addLeaf()播放树叶:playLeaf()两个方法

首先addLeaf()开始随机添加一片树叶,起点X坐标随机取,然后算出Y坐标

    public void addLeaf() {ImageView mLeaf = new ImageView(getContext());Random random = new Random();// 设置随机一片落叶mLeaf.setImageDrawable(mLeafs[random.nextInt(4)]);// 随机设置落叶的起点x坐标float leafX = random.nextInt(mWidthSize);float leafY;// 根据x坐标算出y坐标,因为树叶的范围呈三角形,并且约占高度一半,所以要控制y坐标if (leafX > mWidthSize / 2) {leafY = mHeightSize * 1.0f / mWidthSize * leafX - mHeightSize / 2;} else {leafY = -mHeightSize * 1.0f / mWidthSize * leafX + mHeightSize / 2;}// 设置落叶起点,添加到布局ViewCompat.setX(mLeaf, leafX);ViewCompat.setY(mLeaf, leafY);addView(mLeaf);


Y坐标按照一次方程解出即可,很简单不再阐述。

重点来了,看下动画设置代码

// 设置树叶刚开始出现的动画ObjectAnimator alpha = ObjectAnimator.ofFloat(mLeaf, "alpha", 0.1f, 1);ObjectAnimator scaleX = ObjectAnimator.ofFloat(mLeaf, "scaleX", 0.1f, 1);ObjectAnimator scaleY = ObjectAnimator.ofFloat(mLeaf, "scaleY", 0.1f, 1);AnimatorSet set = new AnimatorSet();set.playTogether(alpha, scaleX, scaleY);set.setDuration(300);// 树叶落下经过的第二个点final PointF pointF1 = new PointF(leafX + random.nextInt((int) (mWidthSize - leafX)), leafY + random.nextInt((int) (mHeightSize - leafY)));// 树叶落下经过的第三个点final PointF pointF2 = new PointF(leafX + random.nextInt((int) (mWidthSize - leafX)), leafY + random.nextInt((int) (mHeightSize - leafY)));// 树叶落下的起点final PointF pointF0 = new PointF(ViewCompat.getX(mLeaf), ViewCompat.getY(mLeaf));// 树叶落下的终点final PointF pointF3 = new PointF(random.nextInt(mWidthSize), mHeightSize);// 通过自定义的贝塞尔估值器算出途经的点的想x,y坐标final BazierTypeEvaluator bazierTypeEvaluator = new BazierTypeEvaluator(pointF1, pointF2);// 设置值动画ValueAnimator bazierAnimator = ValueAnimator.ofObject(bazierTypeEvaluator, pointF0, pointF3);bazierAnimator.setTarget(mLeaf);bazierAnimator.addUpdateListener(new BazierUpdateListener(mLeaf));bazierAnimator.setDuration(2000);// 将以上动画添加到动画集合AnimatorSet allSet = new AnimatorSet();allSet.play(set).before(bazierAnimator);// 随机设置一个补间器allSet.setInterpolator(mInterpolator[random.nextInt(4)]);allSet.addListener(new AnimatorEndListener(mLeaf));allSet.start();

属性动画用到了两个集合,开始是一个树叶生成时缩放透明度的动画,接下来就是值动画的使用,使用到了一个自定义的估值器BazierTypeEvaluator,此货运用了三次方贝塞尔公式算出落叶途经的坐标。贝塞尔是啥呢?我反正不想知道 凸(⊙▂⊙✖ ) ,想简单了解的可以看下爱哥的自定义控件其实很简单5/12,这里直接拿公式套上去就OK了,通过evaluate()的t值变化,算出途经的坐标值。

public class BazierTypeEvaluator implements TypeEvaluator<PointF> {/*** 三次方贝塞尔曲线* B(t)=P0*(1-t)^3+3*P1*t*(1-t)^2+3*P2*t^2*(1-t)+P3*t^3,t∈[0,1]* P0,是我们的起点,* P3是终点,* P1,P2是途径的两个点* 而t则是我们的一个因子,取值范围是0-1*/private PointF pointF1;private PointF pointF2;public BazierTypeEvaluator(PointF pointF1, PointF pointF2) {this.pointF1 = pointF1;this.pointF2 = pointF2;}@Overridepublic PointF evaluate(float t, PointF startValue, PointF endValue) {PointF pointF = new PointF();pointF.x = (float) (startValue.x * Math.pow(1 - t, 3) + 3 * pointF1.x * t * Math.pow(1 - t, 2) + 3 * pointF2.x * Math.pow(t, 2) * (1 - t) + endValue.x * Math.pow(t, 3));pointF.y = (float) (startValue.y * Math.pow(1 - t, 3) + 3 * pointF1.y * t * Math.pow(1 - t, 2) + 3 * pointF2.y * Math.pow(t, 2) * (1 - t) + endValue.y * Math.pow(t, 3));return pointF;}
}

上面bazierAnimator.addUpdateListener(new BazierUpdateListener(mLeaf)),继承ValueAnimator.AnimatorUpdateListener后不断去取算出的坐标值设置给落叶即可,还做了个透明度的变化

    // 值动画更新监听private class BazierUpdateListener implements ValueAnimator.AnimatorUpdateListener {View target;public BazierUpdateListener(View target) {BazierUpdateListener.this.target = target;}@Overridepublic void onAnimationUpdate(ValueAnimator animation) {// 获取坐标,设置落叶的位置final PointF pointF = (PointF) animation.getAnimatedValue();ViewCompat.setX(target, pointF.x);ViewCompat.setY(target, pointF.y);ViewCompat.setAlpha(target, 1 - animation.getAnimatedFraction());}}

allSet.addListener(new AnimatorEndListener(mLeaf));动画集合添加动画停止的监听,用于移除落叶节约资源

    // 动画更新适配器,用于动画停止的时候移除落叶private class AnimatorEndListener extends AnimatorListenerAdapter {View target;public AnimatorEndListener(View target) {this.target = target;}@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);removeView(target);Log.e(TAG, "child:" + getChildCount());}}

播放落叶无非开启子线程不断调用addLeaf()生成落叶

    // 播放落叶,播放15片public void playLeaf() {new Thread() {@Overridepublic void run() {if (mIsDestoryed)// 页面销毁直接返回return;for (int i = 0; i < 15; i++) {if (mIsDestoryed)// 页面销毁直接返回return;((Activity) getContext()).runOnUiThread(new Runnable() {@Overridepublic void run() {addLeaf();}});try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}}}}.start();}

④页面消耗时候的处理,因为有可能在所有落叶在执行动画未完成前用户退出页面了,所以这里暴露方法onDestory()做清理工作

    // 销毁的时候做清理工作public void onDestroy() {Log.e(TAG, "Activity被销毁了");mIsDestoryed = true;if (mAnimatorSets == null) return;for (int i = 0; i < mAnimatorSets.size(); i++) {mAnimatorSets.get(i).cancel();}mAnimatorSets.clear();}

总结:

整体思路不难,重要的是掌握一些有趣的公式结合属性动画做出好玩的效果!

ヽ(^o^)ρ┳┻┳°σ(^o^)/

最后附上资源Demo:飘零落叶控件

属性动画+贝塞尔曲线实现落叶效果~~~(@_@;)相关推荐

  1. android动态波浪效果,android贝塞尔曲线实现波浪效果

    本文实例为大家分享了android贝塞尔曲线实现波浪效果的具体代码,供大家参考,具体内容如下 因为手机录制gif不知道下什么软件好,所以暂时就先忽略效果图了 我在屏幕外多画了1.5个波浪,延伸至屏幕内 ...

  2. android贝塞尔曲线之波浪效果

    1 前言 为了给我以前的博客填坑,这章讲解贝塞尔曲线的几个常用的应用: 1.波浪效果 2.qq聊天列表上的沾粘体效果 3.翻书页效果 4.弹性球效果 大家如果把这些看懂并掌握,以后做和贝塞尔曲线相关的 ...

  3. Android自定义View——贝塞尔曲线实现抛物线效果

    效果展示 原理分析 抛物线效果最主要的难点和原理在于贝塞尔曲线动画的生成,我们通过图片主要讲解贝塞尔曲线动画,这里用到的是二级贝塞尔曲线 1.需要找到贝塞尔曲线的三个点,开启点.结束点.控制点 2.通 ...

  4. android 抛物线轨迹,Android自定义View——贝塞尔曲线实现抛物线效果

    效果展示 原理分析 抛物线效果最主要的难点和原理在于贝塞尔曲线动画的生成,我们通过图片主要讲解贝塞尔曲线动画,这里用到的是二级贝塞尔曲线 1.需要找到贝塞尔曲线的三个点,开启点.结束点.控制点 2.通 ...

  5. css贝塞尔曲线 多个点_了解贝塞尔曲线的数学和Python实现示例

    贝塞尔曲线在计算机图形学中被大量使用,通常可以产生平滑的曲线.如果您曾经使用过Photoshop,则可能会发现名为"锚点"的工具,您可以在其中放置锚点并用它们绘制一些曲线,这些也是 ...

  6. css贝塞尔曲线 多个点_贝塞尔曲线实践

    贝塞尔曲线: 贝塞尔曲线本质上是由线段和节点组成的,形象的说节点是可拖动的支点,线段像可伸缩的皮筋.一个常规的曲线往往由4个控制点构成(p0,p1,p2,p3),曲线经过起点(p0)和终点(p1). ...

  7. java 贝塞尔_java贝塞尔曲线翻页效果

    我写了一个java版本的实现,在android当中效果也实现了.是一样的,你可以把代码当中提示点和辅助线的都打开,我在源码中都注释掉了 public void DrawBezier(int x[],i ...

  8. android波浪动画简书,Android贝塞尔曲线————波浪效果(大波浪)

    Hello大家好,很高兴又一次与大家见面,今天是农历丁酉鸡年(大年初四),现在跟大家拜年有点晚,算是拜晚年,祝大家晚年幸福. 这么快大伙都到了晚年了,Android贝塞尔曲线我也准备以一个大波浪来结束 ...

  9. Android 高级UI解密 (四) :花式玩转贝塞尔曲线(波浪、轨迹变换动画)

    讲解此UI系列必然少不了一个奇妙数学曲线-–贝塞尔曲线,它目前运用于App的范围是在太广了,最初的QQ气泡拖拽,到个人界面的波浪效果.Loading波浪效果,甚至于轨迹变化的动画都可以依赖贝塞尔曲线完 ...

最新文章

  1. makeMtk- user 版本编译
  2. ARM汇编 beq和bne
  3. Maven 项目创建 找不到web.xml
  4. 情人节的第一道小点心
  5. android sdk 目录说明,Android的sdk、api及工程目录说明
  6. java实现行程长度编码,java 实现行程编码解码?
  7. codeigniter mysql查询_php – CodeIgniter MySQL查询不起作用
  8. 提高SQL执行效率的几点建议
  9. 一个基于Spring Boot+Vue+Redis的物联网智能家居系统,可二次开发接私活!
  10. AngularJs 隔离作用域
  11. Postman POST方式提交json数据,PHP接收
  12. java adt eclipse_Eclipse安装ADT插件
  13. 色情版“微信”背后的秘密
  14. 流程表结构设计第一版
  15. 上海理想胡忠顺:聚焦双纬度,剑指智慧化实践
  16. 统计学习(一):最大似然估计
  17. swagger添加权限验证,swagger安全控制
  18. windows环境下设置多个PHP版本的环境变量
  19. 知名软件 XMind 竟然请求其它网站发布其盗版软件
  20. 图片去水印接口,模糊图片中水印

热门文章

  1. 我们用ESP32-Wrover-Kit做自平衡小车犯过的坑
  2. Thinking-in-Java 读书笔记-11-持有对象
  3. oracle 导出数据 utl,oracle 使用 UTL_FILE 导出表数据到txt文件
  4. 查看雾霾用污染地图?用户也需谨慎,莫被仿冒APP污染
  5. 程序员应知必会的思维模型之 18 林纳斯定律 (Linus‘s Law)
  6. excel如何获取括号内字符以及excel如何根据第一列是否相等判断是否累加第二列
  7. 电子元器件分销业ERP系统供应链解决方案
  8. Mac 运行sh文件,也就是传说中的shell脚本
  9. MagicBook屏幕频闪解决方案(Windows、MacOS)
  10. uniapp获取手机网络状态和手机系统信息(如4g,wifi)