Chinaunix首页 | 论坛 | 博客
  • 博客访问: 440670
  • 博文数量: 205
  • 博客积分: 5630
  • 博客等级: 大校
  • 技术积分: 1945
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-06 20:28
文章分类

全部博文(205)

文章存档

2016年(1)

2015年(6)

2014年(9)

2013年(10)

2012年(53)

2011年(25)

2010年(87)

2009年(14)

分类: Android平台

2016-08-24 11:23:43

转载请标明出处: 
http://blog.csdn.net/hanhailong726188/article/details/47363911 
本文出自:海龙的博客

一、概述

最近做了一个比较清新的进度条,没啥难度的,就是涉及到属性动画和canvas绘制圆形的知识,因为群里有一个问怎么实现的,这里就稍微写了一下原理,先看效果图


二、效果图

两个小球旋转动画

Gif录制的帧数有点低,导致稍微有点卡,但是在真实运行的时候一点都不卡


三、实现原理

  1. 自定义view
  2. 自定义属性动画
  3. canvas画圆

四、代码实现 
因为代码非常简单,注释也非常详细,看一下代码就明白了,这里就直接贴代码了:

点击(此处)折叠或打开

  1. package com.hhl.twoballrotationprogress;

  2. import android.animation.AnimatorSet;
  3. import android.animation.ObjectAnimator;
  4. import android.animation.ValueAnimator;
  5. import android.content.Context;
  6. import android.graphics.Canvas;
  7. import android.graphics.Color;
  8. import android.graphics.Paint;
  9. import android.support.annotation.ColorInt;
  10. import android.support.annotation.Keep;
  11. import android.util.AttributeSet;
  12. import android.view.View;
  13. import android.view.animation.DecelerateInterpolator;

  14. /**
  15.  * 两个颜色的小球循环旋转,
  16.  * 目前只支持代码设置颜色、最大半径、最小半径等属性
  17.  * version 1.0
  18.  * //TODO 2.0 添加xml自定义属性支持
  19.  * Created by HanHailong on 15/8/07.
  20.  */
  21. public class TwoBallRotationProgressBar extends View {

  22.     //默认小球最大半径
  23.     private final static int DEFAULT_MAX_RADIUS = 15;
  24.     //默认小球最小半径
  25.     private final static int DEFAULT_MIN_RADIUS = 5;
  26.     //默认两个小球运行轨迹直径距离
  27.     private final static int DEFAULT_DISTANCE = 20;

  28.     //默认第一个小球颜色
  29.     private final static int DEFAULT_ONE_BALL_COLOR = Color.parseColor("#40df73");
  30.     //默认第二个小球颜色
  31.     private final static int DEFAULT_TWO_BALL_COLOR = Color.parseColor("#ffdf3e");

  32.     //默认动画执行时间
  33.     private final static int DEFAULT_ANIMATOR_DURATION = 1000;


  34.     //画笔
  35.     private Paint mPaint;

  36.     //球的最大半径
  37.     private float maxRadius = DEFAULT_MAX_RADIUS;
  38.     //球的最小半径
  39.     private float minRadius = DEFAULT_MIN_RADIUS;

  40.     //两球旋转的范围距离
  41.     private int distance = DEFAULT_DISTANCE;

  42.     //动画的时间
  43.     private long duration = DEFAULT_ANIMATOR_DURATION;

  44.     private Ball mOneBall;
  45.     private Ball mTwoBall;

  46.     private float mCenterX;
  47.     private float mCenterY;

  48.     private AnimatorSet animatorSet;

  49.     public TwoBallRotationProgressBar(Context context) {
  50.         this(context, null);
  51.     }

  52.     public TwoBallRotationProgressBar(Context context, AttributeSet attrs) {
  53.         this(context, attrs, 0);
  54.     }

  55.     public TwoBallRotationProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
  56.         super(context, attrs, defStyleAttr);
  57.         init(context);
  58.     }

  59.     private void init(Context context) {

  60.         mOneBall = new Ball();
  61.         mTwoBall = new Ball();

  62.         mOneBall.setColor(DEFAULT_ONE_BALL_COLOR);
  63.         mTwoBall.setColor(DEFAULT_TWO_BALL_COLOR);

  64.         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

  65.         configAnimator();

  66.     }

  67.     @Override
  68.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  69.         super.onSizeChanged(w, h, oldw, oldh);
  70.         mCenterX = w / 2;
  71.         mCenterY = h / 2;
  72.     }

  73.     @Override
  74.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  75.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  76.         mCenterX = getWidth() / 2;
  77.         mCenterY = getHeight() / 2;
  78.     }

  79.     @Override
  80.     protected void onDraw(Canvas canvas) {
  81.         //画两个小球,半径小的先画,半径大的后画
  82.         if (mOneBall.getRadius() > mTwoBall.getRadius()) {
  83.             mPaint.setColor(mTwoBall.getColor());
  84.             canvas.drawCircle(mTwoBall.getCenterX(), mCenterY, mTwoBall.getRadius(), mPaint);

  85.             mPaint.setColor(mOneBall.getColor());
  86.             canvas.drawCircle(mOneBall.getCenterX(), mCenterY, mOneBall.getRadius(), mPaint);
  87.         } else {
  88.             mPaint.setColor(mOneBall.getColor());
  89.             canvas.drawCircle(mOneBall.getCenterX(), mCenterY, mOneBall.getRadius(), mPaint);

  90.             mPaint.setColor(mTwoBall.getColor());
  91.             canvas.drawCircle(mTwoBall.getCenterX(), mCenterY, mTwoBall.getRadius(), mPaint);
  92.         }
  93.     }

  94.     /**
  95.      * 配置属性动画
  96.      */
  97.     private void configAnimator() {

  98.         //中间半径大小
  99.         float centerRadius = (maxRadius + minRadius) * 0.5f;

  100.         //第一个小球缩放动画,通过改变小球的半径
  101.         //半径变化规律:中间大小->最大->中间大小->最小->中间大小
  102.         ObjectAnimator oneScaleAnimator = ObjectAnimator.ofFloat(mOneBall, "radius",
  103.                 centerRadius, maxRadius, centerRadius, minRadius, centerRadius);
  104.         //无限循环
  105.         oneScaleAnimator.setRepeatCount(ValueAnimator.INFINITE);


  106.         //第一个小球位移动画,通过改变小球的圆心
  107.         ValueAnimator oneCenterAnimator = ValueAnimator.ofFloat(-1, 0, 1, 0, -1);
  108.         oneCenterAnimator.setRepeatCount(ValueAnimator.INFINITE);
  109.         oneCenterAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  110.             @Override
  111.             public void onAnimationUpdate(ValueAnimator animation) {
  112.                 float value = (Float) animation.getAnimatedValue();
  113.                 float x = mCenterX + (distance) * value;
  114.                 mOneBall.setCenterX(x);
  115.                 //不停的刷新view,让view不停的重绘
  116.                 invalidate();
  117.             }
  118.         });

  119.         //第二个小球缩放动画
  120.         //变化规律:中间大小->最小->中间大小->最大->中间大小
  121.         ObjectAnimator twoScaleAnimator = ObjectAnimator.ofFloat(mTwoBall, "radius", centerRadius, minRadius,
  122.                 centerRadius, maxRadius, centerRadius);
  123.         twoScaleAnimator.setRepeatCount(ValueAnimator.INFINITE);

  124.         //第二个小球位移动画
  125.         ValueAnimator twoCenterAnimator = ValueAnimator.ofFloat(1, 0, -1, 0, 1);
  126.         twoCenterAnimator.setRepeatCount(ValueAnimator.INFINITE);
  127.         twoCenterAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  128.             @Override
  129.             public void onAnimationUpdate(ValueAnimator animation) {
  130.                 float value = (Float) animation.getAnimatedValue();
  131.                 float x = mCenterX + (distance) * value;
  132.                 mTwoBall.setCenterX(x);
  133.             }
  134.         });

  135.         //属性动画集合
  136.         animatorSet = new AnimatorSet();
  137.         //四个属性动画一块执行
  138.         animatorSet.playTogether(oneScaleAnimator, oneCenterAnimator, twoScaleAnimator, twoCenterAnimator);
  139.         //动画一次运行时间
  140.         animatorSet.setDuration(DEFAULT_ANIMATOR_DURATION);
  141.         //时间插值器,这里表示动画开始最快,结尾最慢
  142.         animatorSet.setInterpolator(new DecelerateInterpolator());
  143.     }

  144.     /**
  145.      * 小球
  146.      */
  147.     public class Ball {
  148.         @Keep
  149.         private float radius;//半径
  150.         private float centerX;//圆心
  151.         private int color;//颜色

  152.         @Keep
  153.         public float getRadius() {
  154.             return radius;
  155.         }

  156.         @Keep
  157.         public void setRadius(float radius) {
  158.             this.radius = radius;
  159.         }

  160.         public float getCenterX() {
  161.             return centerX;
  162.         }

  163.         public void setCenterX(float centerX) {
  164.             this.centerX = centerX;
  165.         }

  166.         public int getColor() {
  167.             return color;
  168.         }

  169.         public void setColor(int color) {
  170.             this.color = color;
  171.         }
  172.     }

  173.     @Override
  174.     public void setVisibility(int v) {
  175.         if (getVisibility() != v) {
  176.             super.setVisibility(v);
  177.             if (v == GONE || v == INVISIBLE) {
  178.                 stopAnimator();
  179.             } else {
  180.                 startAnimator();
  181.             }
  182.         }
  183.     }

  184.     @Override
  185.     protected void onVisibilityChanged(View changedView, int v) {
  186.         super.onVisibilityChanged(changedView, v);
  187.         if (v == GONE || v == INVISIBLE) {
  188.             stopAnimator();
  189.         } else {
  190.             startAnimator();
  191.         }
  192.     }

  193.     @Override
  194.     protected void onAttachedToWindow() {
  195.         super.onAttachedToWindow();
  196.         startAnimator();
  197.     }

  198.     @Override
  199.     protected void onDetachedFromWindow() {
  200.         super.onDetachedFromWindow();
  201.         stopAnimator();
  202.     }

  203.     /**
  204.      * 设置第一个球的颜色
  205.      *
  206.      * @param color
  207.      */
  208.     public void setOneBallColor(@ColorInt int color) {
  209.         mOneBall.setColor(color);
  210.     }

  211.     /**
  212.      * 设置第二个球的颜色
  213.      *
  214.      * @param color
  215.      */
  216.     public void setmTwoBallColor(@ColorInt int color) {
  217.         mTwoBall.setColor(color);
  218.     }

  219.     /**
  220.      * 设置球的最大半径
  221.      *
  222.      * @param maxRadius
  223.      */
  224.     public void setMaxRadius(float maxRadius) {
  225.         this.maxRadius = maxRadius;
  226.         configAnimator();
  227.     }

  228.     /**
  229.      * 设置球的最小半径
  230.      *
  231.      * @param minRadius
  232.      */
  233.     public void setMinRadius(float minRadius) {
  234.         this.minRadius = minRadius;
  235.         configAnimator();
  236.     }

  237.     /**
  238.      * 设置两个球旋转的最大范围距离
  239.      *
  240.      * @param distance
  241.      */
  242.     public void setDistance(int distance) {
  243.         this.distance = distance;
  244.     }

  245.     public void setDuration(long duration) {
  246.         this.duration = duration;
  247.         if (animatorSet != null) {
  248.             animatorSet.setDuration(duration);
  249.         }
  250.     }

  251.     /**
  252.      * 开始动画
  253.      */
  254.     public void startAnimator() {
  255.         if (getVisibility() != VISIBLE) return;

  256.         if (animatorSet.isRunning()) return;

  257.         if (animatorSet != null) {
  258.             animatorSet.start();
  259.         }
  260.     }

  261.     /**
  262.      * 结束停止动画
  263.      */
  264.     public void stopAnimator() {
  265.         if (animatorSet != null) {
  266.             animatorSet.end();
  267.         }
  268.     }
  269. }

这里需要注意一个地方,因为我们用到了属性动画,在这里:

//第一个小球缩放动画,通过改变小球的半径
        //半径变化规律:中间大小->最大->中间大小->最小->中间大小 ObjectAnimator oneScaleAnimator = ObjectAnimator.ofFloat(mOneBall, "radius",
                centerRadius, maxRadius, centerRadius, minRadius, centerRadius);
其中radius是我们定义的Ball对象的半径属性,这个在混淆的时候会有问题,我们在这里把有关radius都@Keep掉,不让proguard混淆

点击(此处)折叠或打开

  1. /**
  2.      * 小球
  3.      */
  4.     public class Ball {
  5.         //防止混淆
  6.         @Keep
  7.         private float radius;//半径
  8.         private float centerX;//圆心
  9.         private int color;//颜色

  10.         //防止混淆
  11.         @Keep
  12.         public float getRadius() {
  13.             return radius;
  14.         }

  15.         //防止混淆
  16.         @Keep
  17.         public void setRadius(float radius) {
  18.             this.radius = radius;
  19.         }

  20.         public float getCenterX() {
  21.             return centerX;
  22.         }

  23.         public void setCenterX(float centerX) {
  24.             this.centerX = centerX;
  25.         }

  26.         public int getColor() {
  27.             return color;
  28.         }

  29.         public void setColor(int color) {
  30.             this.color = color;
  31.         }
  32.     }

但是目前Gradle没有启动这个插件,所以需要我们手动开启@Keep

五、开始@Keep防止混淆注解

在app/proguard-rules.pro里面添加


点击(此处)折叠或打开

  1. #手动启用support keep注解
  2. #http://tools.android.com/tech-docs/support-annotations
  3. -keep,allowobfuscation @interface android.support.annotation.Keep

  4. -keep @android.support.annotation.Keep class *

  5. -keepclassmembers class * {

  6.     @android.support.annotation.Keep *;
  7. }

这样就实现了一个清新的两球绕中心滚动的进度条了!

最后附上源码下载地址:
阅读(2075) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~