Android仿QQ邮箱下拉刷新动画(三个小球围绕中心转动)

2024-08-22 18:18

本文主要是介绍Android仿QQ邮箱下拉刷新动画(三个小球围绕中心转动),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

仿QQ邮箱下拉刷新动画(三个小球围绕中心转动)

该动画的实现主要借鉴了海龙的博客- 两个小球不停的绕中心旋转的进度条 ,在此感谢下。

1 首先上图(折腾了好久才把gif给搞了上去

这里写图片描述

2 分析

2.1 当我们看到一个动画,首先需要对动画的效果进行分析,而不是盲目的进行开发

2.2 动画的分解(先需要关注一个小球的效果,避免其他干扰)
2.2.1 平移动画:把中心点的横坐标当作坐标的原点
第一个小球的x轴变化为:-1f>0f>1f>0f>-1f;
第二个小球的x轴变化为:0f>1f>0f>-1f>0f;
第三个小球的x轴变化为:1f>0f>-1f>0f>1f;
2.2.2 缩放动画:(三种大小:minRadius,centerRadius,maxRadius)
第一个小球的缩放变化为:center>max>center>min>center;
第二个小球的缩放变化为:max>center>min>center>max;
第三个小球的缩放变化为:center>min>center>max>center;
2.2.3 重要的一点:简单理解为半径大的覆盖在半径小的上方

3 分析完毕,直接上代码

public class ThreeBallRotationProgressBar extends View {private final static int DEFAULT_MAX_RADIUS = 16;private final static int DEFAULT_MIN_RADIUS = 5;private final static int DEFAULT_DISTANCE = 35;private final static int DEFAULT_ONE_BALL_COLOR = Color.parseColor("#40df73");private final static int DEFAULT_TWO_BALL_COLOR = Color.parseColor("#ffdf3e");private final static int DEFAULT_THREE_BALL_COLOR = Color.parseColor("#ff733e");private final static int DEFAULT_ANIMATOR_DURATION = 1400;private Paint mOnePaint;private Paint mTwoPaint;private Paint mThreePaint;private float maxRadius = DEFAULT_MAX_RADIUS;private float minRadius = DEFAULT_MIN_RADIUS;private int distance = DEFAULT_DISTANCE;private long duration = DEFAULT_ANIMATOR_DURATION;private Ball mOneBall;private Ball mTwoBall;private Ball mThreeBall;private float mCenterX;private float mCenterY;private AnimatorSet animatorSet;public ThreeBallRotationProgressBar(Context context) {this(context, null);}public ThreeBallRotationProgressBar(Context context, AttributeSet attrs) {this(context, attrs, 0);}public ThreeBallRotationProgressBar(Context context, AttributeSet attrs,int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}private void init(Context context) {mOneBall = new Ball();mTwoBall = new Ball();mThreeBall = new Ball();mOneBall.setColor(DEFAULT_ONE_BALL_COLOR);mTwoBall.setColor(DEFAULT_TWO_BALL_COLOR);mThreeBall.setColor(DEFAULT_THREE_BALL_COLOR);mOnePaint = new Paint(Paint.ANTI_ALIAS_FLAG);mOnePaint.setColor(DEFAULT_ONE_BALL_COLOR);mTwoPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mTwoPaint.setColor(DEFAULT_TWO_BALL_COLOR);mThreePaint = new Paint(Paint.ANTI_ALIAS_FLAG);mThreePaint.setColor(DEFAULT_THREE_BALL_COLOR);configAnimator();}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mCenterX = w / 2;mCenterY = h / 2;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);mCenterX = getWidth() / 2;mCenterY = getHeight() / 2;}@Overrideprotected void onDraw(Canvas canvas) {if (mOneBall.getRadius() >= mTwoBall.getRadius()) {if (mThreeBall.getRadius() >= mOneBall.getRadius()) {canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,mTwoBall.getRadius(), mTwoPaint);canvas.drawCircle(mOneBall.getCenterX(), mCenterY,mOneBall.getRadius(), mOnePaint);canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,mThreeBall.getRadius(), mThreePaint);} else {if (mTwoBall.getRadius() <= mThreeBall.getRadius()) {canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,mTwoBall.getRadius(), mTwoPaint);canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,mThreeBall.getRadius(), mThreePaint);canvas.drawCircle(mOneBall.getCenterX(), mCenterY,mOneBall.getRadius(), mOnePaint);} else {canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,mThreeBall.getRadius(), mThreePaint);canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,mTwoBall.getRadius(), mTwoPaint);canvas.drawCircle(mOneBall.getCenterX(), mCenterY,mOneBall.getRadius(), mOnePaint);}}} else {if (mThreeBall.getRadius() >= mTwoBall.getRadius()) {canvas.drawCircle(mOneBall.getCenterX(), mCenterY,mOneBall.getRadius(), mOnePaint);canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,mTwoBall.getRadius(), mTwoPaint);canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,mThreeBall.getRadius(), mThreePaint);} else {if (mOneBall.getRadius() <= mThreeBall.getRadius()) {canvas.drawCircle(mOneBall.getCenterX(), mCenterY,mOneBall.getRadius(), mOnePaint);canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,mThreeBall.getRadius(), mThreePaint);canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,mTwoBall.getRadius(), mTwoPaint);} else {canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,mThreeBall.getRadius(), mThreePaint);canvas.drawCircle(mOneBall.getCenterX(), mCenterY,mOneBall.getRadius(), mOnePaint);canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,mTwoBall.getRadius(), mTwoPaint);}}}}private void configAnimator() {float centerRadius = (maxRadius + minRadius) * 0.5f;ObjectAnimator oneScaleAnimator = ObjectAnimator.ofFloat(mOneBall,"radius", centerRadius, maxRadius, centerRadius, minRadius,centerRadius);oneScaleAnimator.setRepeatCount(ValueAnimator.INFINITE);ValueAnimator oneCenterAnimator = ValueAnimator.ofFloat(-1, 0, 1, 0, -1);oneCenterAnimator.setRepeatCount(ValueAnimator.INFINITE);oneCenterAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float value = (Float) animation.getAnimatedValue();float x = mCenterX + (distance) * value;mOneBall.setCenterX(x);invalidate();}});ValueAnimator oneAlphaAnimator = ValueAnimator.ofFloat(0.8f, 1, 0.8f,0, 0.8f);oneAlphaAnimator.setRepeatCount(ValueAnimator.INFINITE);oneAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float value = (Float) animation.getAnimatedValue();int alpha = (int) (255 * value);mOnePaint.setAlpha(alpha);}});ObjectAnimator twoScaleAnimator = ObjectAnimator.ofFloat(mTwoBall,"radius", maxRadius, centerRadius, minRadius, centerRadius,maxRadius);twoScaleAnimator.setRepeatCount(ValueAnimator.INFINITE);ValueAnimator twoCenterAnimator = ValueAnimator.ofFloat(0, 1, 0, -1, 0);twoCenterAnimator.setRepeatCount(ValueAnimator.INFINITE);twoCenterAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float value = (Float) animation.getAnimatedValue();float x = mCenterX + (distance) * value;mTwoBall.setCenterX(x);}});ValueAnimator twoAlphaAnimator = ValueAnimator.ofFloat(1, 0.8f, 0,0.8f, 1);twoAlphaAnimator.setRepeatCount(ValueAnimator.INFINITE);twoAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float value = (Float) animation.getAnimatedValue();int alpha = (int) (255 * value);mTwoPaint.setAlpha(alpha);}});ObjectAnimator threeScaleAnimator = ObjectAnimator.ofFloat(mThreeBall,"radius", centerRadius, minRadius, centerRadius, maxRadius,centerRadius);threeScaleAnimator.setRepeatCount(ValueAnimator.INFINITE);ValueAnimator threeCenterAnimator = ValueAnimator.ofFloat(1, 0, -1, 0,1);threeCenterAnimator.setRepeatCount(ValueAnimator.INFINITE);threeCenterAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float value = (Float) animation.getAnimatedValue();float x = mCenterX + (distance) * value;mThreeBall.setCenterX(x);}});ValueAnimator threeAlphaAnimator = ValueAnimator.ofFloat(0.8f, 0, 0.8f,1, 0.8f);threeAlphaAnimator.setRepeatCount(ValueAnimator.INFINITE);threeAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float value = (Float) animation.getAnimatedValue();int alpha = (int) (255 * value);mThreePaint.setAlpha(alpha);}});animatorSet = new AnimatorSet();animatorSet.playTogether(oneScaleAnimator, oneCenterAnimator,twoScaleAnimator, twoCenterAnimator, threeScaleAnimator,threeCenterAnimator);animatorSet.setDuration(DEFAULT_ANIMATOR_DURATION);animatorSet.setInterpolator(new LinearInterpolator());}public class Ball {private float radius;private float centerX;private int color;public float getRadius() {return radius;}public void setRadius(float radius) {this.radius = radius;}public float getCenterX() {return centerX;}public void setCenterX(float centerX) {this.centerX = centerX;}public int getColor() {return color;}public void setColor(int color) {this.color = color;}}@Overridepublic void setVisibility(int v) {if (getVisibility() != v) {super.setVisibility(v);if (v == GONE || v == INVISIBLE) {stopAnimator();} else {startAnimator();}}}@Overrideprotected void onVisibilityChanged(View changedView, int v) {super.onVisibilityChanged(changedView, v);if (v == GONE || v == INVISIBLE) {stopAnimator();} else {startAnimator();}}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();startAnimator();}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();stopAnimator();}public void setOneBallColor(int color) {mOneBall.setColor(color);}public void setmTwoBallColor(int color) {mTwoBall.setColor(color);}public void setMaxRadius(float maxRadius) {this.maxRadius = maxRadius;configAnimator();}public void setMinRadius(float minRadius) {this.minRadius = minRadius;configAnimator();}public void setDistance(int distance) {this.distance = distance;}public void setDuration(long duration) {this.duration = duration;if (animatorSet != null) {animatorSet.setDuration(duration);}}public void startAnimator() {if (getVisibility() != VISIBLE)return;if (animatorSet.isRunning())return;if (animatorSet != null) {animatorSet.start();}}public void stopAnimator() {if (animatorSet != null) {animatorSet.end();}}
}

3.1 configAnimator()方法主要就是实现2中分析的动画效果

3.2 为了解决2中提到的重要一点,半径大的小球覆盖在半径小的小球上方,主要在onDraw()采用比较的方式实现

4 总结

4.1 掌阅iReader的下拉刷新也采用了类似的动画效果(三个方形围绕中心转动),大家可以参考本文章,试着实现里边的动画效果。相信自己写过,总能有不少收获的~

4.2 对于android动画,还是需要耐心的分析,当然熟悉的掌握动画实现还是必要的!

5 源码下载

下载地址

这篇关于Android仿QQ邮箱下拉刷新动画(三个小球围绕中心转动)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1097024

相关文章

最好用的WPF加载动画功能

《最好用的WPF加载动画功能》当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给... 目录前言需求分析高级用法综合案例总结最后前言当开发应用程序时,提供良好的用户体验(UX)是至关重要

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主

无线路由器哪个品牌好用信号强? 口碑最好的三个路由器大比拼

《无线路由器哪个品牌好用信号强?口碑最好的三个路由器大比拼》不同品牌在信号覆盖、稳定性和易用性等方面各有特色,如何在众多选择中找到最适合自己的那款无线路由器呢?今天推荐三款路由器让你的网速起飞... 今天我们来聊聊那些让网速飞起来的路由器。在这个信息爆炸的时代,一个好路由器简直就是家庭网编程络的心脏。无论你

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)

《Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)》:本文主要介绍Python基于火山引擎豆包大模型搭建QQ机器人详细的相关资料,包括开通模型、配置APIKEY鉴权和SD... 目录豆包大模型概述开通模型付费安装 SDK 环境配置 API KEY 鉴权Ark 模型接口Prompt

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

Qt QWidget实现图片旋转动画

《QtQWidget实现图片旋转动画》这篇文章主要为大家详细介绍了如何使用了Qt和QWidget实现图片旋转动画效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、效果展示二、源码分享本例程通过QGraphicsView实现svg格式图片旋转。.hpjavascript

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

跨国公司撤出在华研发中心的启示:中国IT产业的挑战与机遇

近日,IBM中国宣布撤出在华的两大研发中心,这一决定在IT行业引发了广泛的讨论和关注。跨国公司在华研发中心的撤出,不仅对众多IT从业者的职业发展带来了直接的冲击,也引发了人们对全球化背景下中国IT产业竞争力和未来发展方向的深思。面对这一突如其来的变化,我们应如何看待跨国公司的决策?中国IT人才又该如何应对?中国IT产业将何去何从?本文将围绕这些问题展开探讨。 跨国公司撤出的背景与

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo