Android实现跳动的小球加载动画效果

先来看看效果图

跳动的小球做这个动画,需掌握:

1、属性动画

2、Path类、Canvas类

3、贝塞尔曲线

4、SurfaceView用法

5、自定义attr属性

6 、架构: 状态模式,控制器

7 、自由落体,抛物线等概念

不多说了,直接上码

1.DancingView.java

public class DancingView extends SurfaceView implements SurfaceHolder.Callback { public static final int STATE_DOWN = 1;//向下状态 public static final int STATE_UP = 2;//向上状态 public static final int DEFAULT_POINT_RADIUS = 10; public static final int DEFAULT_BALL_RADIUS = 13; public static final int DEFAULT_LINE_WIDTH = 200; public static final int DEFAULT_LINE_HEIGHT = 2; public static final int DEFAULT_LINE_COLOR = Color.parseColor("#FF9800"); public static final int DEFAULT_POINT_COLOR = Color.parseColor("#9C27B0"); public static final int DEFAULT_BALL_COLOR = Color.parseColor("#FF4081"); public static final int DEFAULT_DOWN_DURATION = 600;//ms public static final int DEFAULT_UP_DURATION = 600;//ms public static final int DEFAULT_FREEDOWN_DURATION = 1000;//ms public static final int MAX_OFFSET_Y = 50;//水平下降最大偏移距离 public int PONIT_RADIUS = DEFAULT_POINT_RADIUS;//小球半径 public int BALL_RADIUS = DEFAULT_BALL_RADIUS;//小球半径 private Paint mPaint; private Path mPath; private int mLineColor; private int mPonitColor; private int mBallColor; private int mLineWidth; private int mLineHeight; private float mDownDistance; private float mUpDistance; private float freeBallDistance; private ValueAnimator mDownController;//下落控制器 private ValueAnimator mUpController;//上弹控制器 private ValueAnimator mFreeDownController;//自由落体控制器 private AnimatorSet animatorSet; private int state; private boolean ismUpControllerDied = false; private boolean isAnimationShowing = false; private boolean isBounced = false; private boolean isBallFreeUp = false; public DancingView(Context context) { super(context); init(context, null); } public DancingView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public DancingView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { initAttributes(context, attrs); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(mLineHeight); mPaint.setStrokeCap(Paint.Cap.ROUND); mPath = new Path(); getHolder().addCallback(this); initController(); } private void initAttributes(Context context, AttributeSet attrs) { TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.DancingView); mLineColor = typeArray.getColor(R.styleable.DancingView_lineColor, DEFAULT_LINE_COLOR); mLineWidth = typeArray.getDimensionPixelOffset(R.styleable.DancingView_lineWidth, DEFAULT_LINE_WIDTH); mLineHeight = typeArray.getDimensionPixelOffset(R.styleable.DancingView_lineHeight, DEFAULT_LINE_HEIGHT); mPonitColor = typeArray.getColor(R.styleable.DancingView_pointColor, DEFAULT_POINT_COLOR); mBallColor = typeArray.getColor(R.styleable.DancingView_ballColor, DEFAULT_BALL_COLOR); typeArray.recycle(); } private void initController() { mDownController = ValueAnimator.ofFloat(0, 1); mDownController.setDuration(DEFAULT_DOWN_DURATION); mDownController.setInterpolator(new DecelerateInterpolator()); mDownController.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mDownDistance = MAX_OFFSET_Y * (float) animation.getAnimatedValue(); postInvalidate(); } }); mDownController.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { state = STATE_DOWN; } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); mUpController = ValueAnimator.ofFloat(0, 1); mUpController.setDuration(DEFAULT_UP_DURATION); mUpController.setInterpolator(new DancingInterpolator()); mUpController.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mUpDistance = MAX_OFFSET_Y * (float) animation.getAnimatedValue(); if (mUpDistance >= MAX_OFFSET_Y) { //进入自由落体状态 isBounced = true; if (!mFreeDownController.isRunning() && !mFreeDownController.isStarted() && !isBallFreeUp) { mFreeDownController.start(); } } postInvalidate(); } }); mUpController.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { state = STATE_UP; } @Override public void onAnimationEnd(Animator animation) { ismUpControllerDied = true; } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); mFreeDownController = ValueAnimator.ofFloat(0, 8f); mFreeDownController.setDuration(DEFAULT_FREEDOWN_DURATION); mFreeDownController.setInterpolator(new DecelerateInterpolator()); mFreeDownController.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //该公式解决上升减速 和 下降加速 float t = (float) animation.getAnimatedValue(); freeBallDistance = 40 * t - 5 * t * t; if (ismUpControllerDied) {//往上抛,到临界点 postInvalidate(); } } }); mFreeDownController.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { isBallFreeUp = true; } @Override public void onAnimationEnd(Animator animation) { isAnimationShowing = false; //循环第二次 startAnimations(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); animatorSet = new AnimatorSet(); animatorSet.play(mDownController).before(mUpController); animatorSet.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { isAnimationShowing = true; } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } /** * 启动动画,外部调用 */ public void startAnimations() { if (isAnimationShowing) { return; } if (animatorSet.isRunning()) { animatorSet.end(); animatorSet.cancel(); } isBounced = false; isBallFreeUp = false; ismUpControllerDied = false; animatorSet.start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 一条绳子用左右两部分的二阶贝塞尔曲线组成 mPaint.setColor(mLineColor); mPath.reset(); //起始点 mPath.moveTo(getWidth() / 2 - mLineWidth / 2, getHeight() / 2); if (state == STATE_DOWN) {//下落 /**************绘制绳子开始*************/ //左部分 的贝塞尔 mPath.quadTo((float) (getWidth() / 2 - mLineWidth / 2 + mLineWidth * 0.375), getHeight() / 2 + mDownDistance, getWidth() / 2, getHeight() / 2 + mDownDistance); //右部分 的贝塞尔 mPath.quadTo((float) (getWidth() / 2 + mLineWidth / 2 - mLineWidth * 0.375), getHeight() / 2 + mDownDistance, getWidth() / 2 + mLineWidth / 2, getHeight() / 2); mPaint.setStyle(Paint.Style.STROKE); canvas.drawPath(mPath, mPaint); /**************绘制绳子结束*************/ /**************绘制弹跳小球开始*************/ mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(mBallColor); canvas.drawCircle(getWidth() / 2, getHeight() / 2 + mDownDistance - BALL_RADIUS, BALL_RADIUS, mPaint); /**************绘制弹跳小球结束*************/ } else if (state == STATE_UP) { //向上弹 /**************绘制绳子开始*************/ //左部分的贝塞尔 mPath.quadTo((float) (getWidth() / 2 - mLineWidth / 2 + mLineWidth * 0.375), getHeight() / 2 + 50 - mUpDistance, getWidth() / 2, getHeight() / 2 + (50 - mUpDistance)); //右部分的贝塞尔 mPath.quadTo((float) (getWidth() / 2 + mLineWidth / 2 - mLineWidth * 0.375), getHeight() / 2 + 50 - mUpDistance, getWidth() / 2 + mLineWidth / 2, getHeight() / 2); mPaint.setStyle(Paint.Style.STROKE); canvas.drawPath(mPath, mPaint); /**************绘制绳子结束*************/ mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(mBallColor); //弹性小球,自由落体 if (!isBounced) { //上升 canvas.drawCircle(getWidth() / 2, getHeight() / 2 + (MAX_OFFSET_Y - mUpDistance) - BALL_RADIUS, BALL_RADIUS, mPaint); } else { //自由落体 canvas.drawCircle(getWidth() / 2, getHeight() / 2 - freeBallDistance - BALL_RADIUS, BALL_RADIUS, mPaint); } } mPaint.setColor(mPonitColor); mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(getWidth() / 2 - mLineWidth / 2, getHeight() / 2, PONIT_RADIUS, mPaint); canvas.drawCircle(getWidth() / 2 + mLineWidth / 2, getHeight() / 2, PONIT_RADIUS, mPaint); } @Override public void surfaceCreated(SurfaceHolder holder) { Canvas canvas = holder.lockCanvas();//锁定整个SurfaceView对象,获取该Surface上的Canvas. draw(canvas); holder.unlockCanvasAndPost(canvas);//释放画布,提交修改 } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }

2.DancingInterpolator.java

public class DancingInterpolator implements Interpolator { @Override public float getInterpolation(float input) { return (float) (1 - Math.exp(-3 * input) * Math.cos(10 * input)); } }

3.自定义属性 styles.xml

<declare-styleable name="DancingView"> <attr name="lineWidth" format="dimension" /> <attr name="lineHeight" format="dimension" /> <attr name="pointColor" format="reference|color" /> <attr name="lineColor" format="reference|color" /> <attr name="ballColor" format="reference|color" /> </declare-styleable>

注意:颜色、尺寸、参数可以自己测试,调整。

以上就是本文的全部内容,希望对大家的学习和工作能有所帮助哦。

时间: 2024-10-30 09:52:00

Android实现跳动的小球加载动画效果的相关文章

android仿爱奇艺加载动画实例_Android

本篇文章介绍了android仿爱奇艺加载动画实例,具体代码如下: 效果图: 用到的知识点: Path ValueAnimator 如果对Path和ValueAnimator还不熟悉推荐去看这几个大神的Blog自定义view的目前讲的最适合我的文章 ,自定义view的详细教程和实践,这个也是教程和实践,感谢他们的付出!(希望大家可以认真看完,可以得到很多启发). 拆解动画 一个圆先顺时针的慢慢画出来(圆不是一个闭合的圆) 这一步是一个组合动画,圆慢慢的消失,同时三角形顺时针旋转 这里的难点主要就是

Android仿支付宝笑脸刷新加载动画的实现代码_Android

看到支付宝的下拉刷新有一个笑脸的动画,因此自己也动手实现一下.效果图如下: 一.总体思路 1.静态部分的笑脸. 这一部分的笑脸就是一个半圆弧,加上两颗眼睛,这部分比较简单,用于一开始的展示. 2.动态笑脸的实现. 2.1.先是从底部有一个圆形在运动,运动在左眼位置时把左眼给绘制,同时圆形继续运动,运动到右眼位置时绘制右眼,圆形继续运动到最右边的位置. 2.2.当上面的圆形运动到最右边时候,开始不断绘制脸,从右向左,脸不断增长,这里脸设置为接近半个圆形的大小. 2.3.当脸画完的时候,开始让脸旋转

Android仿网易一元夺宝客户端下拉加载动画效果(一)_Android

上上周写的一个demo,仿照网易一元夺宝的下拉刷新效果. 原效果是(第一部分)一个小太阳拉下来,然后松开回弹上去, (第二部分)再掉下来一个硬币进行中轴旋转. 本文实现的效果的是第一部分的,效果演示图如下: Gif图看起来比较卡顿...其实真机演示效果还是很流畅的. 下面分析实现过程: 当时因为时间有限没有写在下拉刷新的组件中,也没有封装成一个单独的组件,只是在主布局后面写了一个View然后实现相应的操作,进行封装并不难,这里就不花时间BB了,下面是布局文件: <RelativeLayout x

Android仿支付宝笑脸刷新加载动画的实现代码

看到支付宝的下拉刷新有一个笑脸的动画,因此自己也动手实现一下.效果图如下: 一.总体思路 1.静态部分的笑脸. 这一部分的笑脸就是一个半圆弧,加上两颗眼睛,这部分比较简单,用于一开始的展示. 2.动态笑脸的实现. 2.1.先是从底部有一个圆形在运动,运动在左眼位置时把左眼给绘制,同时圆形继续运动,运动到右眼位置时绘制右眼,圆形继续运动到最右边的位置. 2.2.当上面的圆形运动到最右边时候,开始不断绘制脸,从右向左,脸不断增长,这里脸设置为接近半个圆形的大小. 2.3.当脸画完的时候,开始让脸旋转

android仿爱奇艺加载动画实例

本篇文章介绍了android仿爱奇艺加载动画实例,具体代码如下: 效果图: 用到的知识点: Path ValueAnimator 如果对Path和ValueAnimator还不熟悉推荐去看这几个大神的Blog自定义view的目前讲的最适合我的文章 ,自定义view的详细教程和实践,这个也是教程和实践,感谢他们的付出!(希望大家可以认真看完,可以得到很多启发). 拆解动画 一个圆先顺时针的慢慢画出来(圆不是一个闭合的圆) 这一步是一个组合动画,圆慢慢的消失,同时三角形顺时针旋转 这里的难点主要就是

jQuery生成假加载动画效果_jquery

在使用PDFObject.js时,由于后台需要转换数据,在前台显示的时候,有很长一段时间显示空白页面,所以想到写一个假的加载动画 script片段: <script type="text/javascript"> var bar = 0; var line = "||" ; var amount ="||" ; function count(){ bar= bar+2 ; amount =amount + line; $("

CSS3制作loading加载动画效果代码

  在我们这次的新设计教程中,我将向您展示如何创建纯CSS3的loading加载动画组件(没有任何图像).我认为它可以为你减少项目的代码量和额外的图像对你网站的负载.我准备了三种不同风格的加载组件.现在,让我们看看我做的. css3-loading Step 1. HTML 你可以在这里看到的三个元素–放置"加载"元素的div.  代码如下   <div class="main_body">     <div class="element

android 仿淘宝的加载刷新效果

自定义view: package com.taobao.view; import android.content.Context; import android.util.AttributeSet; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.LinearInterpolator; import androi

WEBJX收集jQuery的加载动画和进度条插件

文章简介:精心挑选的8款jQuery 加载动画和进度条插件. 加载动画和进度条在网站和 Web 应用中的使用非常流行.虽然网速越来越快,但是我们的网站越来越复杂,同时用户对网站的使用体验的要求也越来越高.在内容加载缓慢的时候,使用时尚的加载动画和进度条告诉用户还有内容正在加载是一种非常好的方式.今天这篇文章向大家推荐8款基于jQuery实现的加载动画和进度条插件. Spin.js 最喜欢这款插件了,动画图片的长度.粗细.速度和角度都可以灵活控制,想要做成什么样都可以. 源码下载 在线演示 Per