Android仿QQ邮箱下拉刷新动画(三个小球围绕中心转动)
来源:互联网 发布:电商软件营销方案 编辑:程序博客网 时间:2024/04/30 16:03
仿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(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mCenterX = w / 2; mCenterY = h / 2; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mCenterX = getWidth() / 2; mCenterY = getHeight() / 2; } @Override protected 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() { @Override public 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() { @Override public 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() { @Override public 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() { @Override public 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() { @Override public 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() { @Override public 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; } } @Override public void setVisibility(int v) { if (getVisibility() != v) { super.setVisibility(v); if (v == GONE || v == INVISIBLE) { stopAnimator(); } else { startAnimator(); } } } @Override protected void onVisibilityChanged(View changedView, int v) { super.onVisibilityChanged(changedView, v); if (v == GONE || v == INVISIBLE) { stopAnimator(); } else { startAnimator(); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); startAnimator(); } @Override protected 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 源码下载
下载地址
0 0
- Android仿QQ邮箱下拉刷新动画(三个小球围绕中心转动)
- (转)android 围绕中心旋转动画
- 围绕中心匀速,加速,减速转动的动画
- android 围绕中心旋转动画
- android 围绕中心旋转动画
- android 围绕中心旋转动画
- android 围绕中心旋转动画
- Android高仿QQ下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android 高仿QQ的下拉刷新 ListView
- Android 轻松实现仿QQ消息下拉刷新
- Android 轻松实现仿QQ空间下拉刷新
- Android仿QQ下拉刷新、上拉回弹
- CTF 【每日一题20160627】简单的ELF逆向
- RangeError: index out of range
- CSS的position
- 深入浅出iOS事件机制
- Android 获取手机传感器信息
- Android仿QQ邮箱下拉刷新动画(三个小球围绕中心转动)
- Golang 通关初级(1)
- 面试题:“你能不能谈谈,java GC是在什么时候,对什么东西,做了什么事情?”
- GibbsLDA utils.h分析
- 关闭TV的效果
- golang 中 reflect 简单用法
- 使用pthreads实现真正的PHP多线程(需PHP5.3以上版本)
- github操作分享
- 浏览器调试教程-chrome