码迷,mamicode.com
首页 > 其他好文 > 详细

一个不是那么优美的圆形进度条续(基本还原原应用里面的效果)

时间:2016-07-25 14:46:49      阅读:234      评论:0      收藏:0      [点我收藏+]

标签:

之前帮别人写了一个不是那么优美的圆形进度条,效果图大家也看过了。但是后某人不满意,说原应用是倒计时时间最后5s,才开始显示数字的,同时转完一圈需要的时间只能是30s左右。然后我掐时间看了一下虽然总时间设置的是30s,但是总共转完一圈却耗费了50多秒的样子。
问题出来了:
1. 转圈总时间30s不正确
2. 数字显示时间不正确
3. 数字1的动画没原应用的好(2144手机令牌)

花了一个小时搞了一下,忍不住终于射出来了什么东西,解决了上面的3个问题(请看1从有到无);国际惯例效果图先行,先看下改善之后的效果图(不好请不要吐我口水)
技术分享

当对于一个问题无从下手的话,有时候反编译看下别人家的源代码也是不错的选择。反编译工具用的是Android逆向助手。
看了下它的代码,发现他原来是用定时器区去完成这个工作的

 public class MyProgressCount extends CountDownTimer
  {
    public MyProgressCount()
    {
      super(30000L, 25L);
    }

    public void onFinish()
    {
      start();
    }

    public void onTick(long paramLong)
    {
      MainFragment.this.count = (MainFragment.this.max - (float)paramLong);
      MainFragment.this.progress_bar.setProgress(MainFragment.this.count);
      MainFragment.this.circleRadius = (MainFragment.this.progressBar_width / 2);
      MainFragment.this.circleX = (MainFragment.this.window_width / 2 - MainFragment.this.circleRadius);
      MainFragment.this.circleY = (MainFragment.this.circleY_init + MainFragment.this.circleRadius);
      if (MainFragment.this.count >= MainFragment.this.max - 200.0F)
      {
        MainFragment.this.round_text.setVisibility(8);
        MainFragment.this.password_time = (DateUtils.getStringToDate(DateUtils.getCurrentDate()) + MainFragment.this.d_value);
        MainFragment.this.count = 0.0F;
        MainFragment.this.password = Util.DynamicPassword(MainFragment.this.uid, MainFragment.this.password_time - (MainFragment.this.password_time % 30L), MainFragment.this.token);
        DbManage.getInstance(MainFragment.this.activity).saveActionStr(MainFragment.this.password, MainFragment.this.password_time - (MainFragment.this.password_time % 30L));
        MainFragment.this.stringArr = MainFragment.this.password.toCharArray();
        MainFragment.this.dynamic_password.setText(MainFragment.this.stringArr[0] + " " + MainFragment.this.stringArr[1] + " " + MainFragment.this.stringArr[2] + "   " + MainFragment.this.stringArr[3] + " " + MainFragment.this.stringArr[4] + " " + MainFragment.this.stringArr[5]);
      }
      if (MainFragment.this.count > MainFragment.this.max * 5.0F / 6.0F)
      {
        float f = MainFragment.this.max * 5.0F / 6.0F;
        f = (float)(MainFragment.this.count / 1000.0D);
        double d = 3.141592653589793D * 12.0F * f / 180.0D;
        int i = (int)(Math.sin(d) * MainFragment.this.progress_bar.getWidth() / 2.0D);
        int j = (int)(Math.cos(d) * MainFragment.this.progress_bar.getWidth() / 2.0D);
        Util.setLayout(MainFragment.this.round_text, MainFragment.this.circleX + MainFragment.this.circleRadius - Math.abs(i), MainFragment.this.circleY - Math.abs(j));
        i = ((Integer)ColorUtils.evaluate((float)((MainFragment.this.count / MainFragment.this.max - 0.75D) * 4.0D), Integer.valueOf(-1853686), Integer.valueOf(-3407872))).intValue();
        MainFragment.this.round_text.setBackgroundDrawable(Util.getRoundBg(i, 100));
        MainFragment.this.round_text.setTextColor(-1);
        if ((f >= 25.0F) && (f <= 30.0F))
        {
          MainFragment.this.round_text.setVisibility(0);
          MainFragment.this.round_text.setText((int)(31.0F - f));
          if ((f >= 29.0F) && (f < 30.0F) && (!(MainFragment.this.scaleAnimation.hasStarted())))
          {
            MainFragment.this.round_text.setAnimation(MainFragment.this.scaleAnimation);
            MainFragment.this.scaleAnimation.setAnimationListener(new Animation.AnimationListener()
            {
              public void onAnimationEnd(Animation paramAnimation)
              {
                MainFragment.this.scaleAnimation = new ScaleAnimation(1.0F, 0.0F, 1.0F, 0.0F, 1, 0.5F, 1, 0.5F);
                MainFragment.this.scaleAnimation.setDuration(MainFragment.this.duration);
              }

              public void onAnimationRepeat(Animation paramAnimation)
              {
              }

              public void onAnimationStart(Animation paramAnimation)
              {
              }
            });
            MainFragment.this.scaleAnimation.start();
          }
        }
        while (true)
        {
          MainFragment.this.round_text.invalidate();
          return;
          MainFragment.this.round_text.setVisibility(8);
        }
      }
      MainFragment.this.round_text.setVisibility(8);
    }
  }

至于里面数字的显示用的是一个TextView动态改变他的位置以及内容和背景颜色,然后配合上CircleProgressBar就行了(当然里面涉及到有颜色值改变的计算),详情看下文给出的源代码。

当然看了反编译代码思路说起来很轻松,估计是个稍微会一点安卓的人都知道,那下面我们改进入正题了(talk is cheap,show me the fucking code).

里面牵涉到4个类,工具类:UtilColorUtils;界面类:TestActivity01CircleProgressBar
先看下CircleProgressBar里面有些什么狗屎东西:

package com.example.tangxb.myapplication;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;

public class CircleProgressBar extends View {
    private static final int DEFAULT_BACKGROUND_COLOR = Color.parseColor("#ffffff");
    private static final int DEFAULT_MAX = 100;
    private static final int DEFAULT_PROGRESS_COLOR = Color.parseColor("#54bfad");
    private static final int DEFAULT_PROGRESS_END_COLOR = 0;
    private static final int DEFAULT_SIZE = 80;
    private static final boolean DEFAULT_TRANSITION_ENABLE = false;
    private static final int END_ANGLE = 360;
    private static final float MAX_SWEEP_ANGLE = 360.0F;
    private static final String TAG = "ArcProgressBar";
    private float DEFAULT_STROKE_WIDTH;
    int c;
    Context context;
    SizeChangeListener l;
    private float mMax;
    private Path mPath;
    private float mProgress;
    private int mProgressBackgroundColor;
    private Paint mProgressBgPaint;
    private RectF mProgressBgRectF;
    private int mProgressColor;
    private int mProgressEndColor;
    private int mProgressEndColor2;
    private Paint mProgressPaint;
    private Paint mProgressPaintEnd;
    private RectF mProgressRectF;
    private float mStrokeWith;
    private float unitAngle;


    public CircleProgressBar(Context paramContext) {
        this(paramContext, null);
    }

    public CircleProgressBar(Context paramContext, AttributeSet paramAttributeSet) {
        this(paramContext, paramAttributeSet, 0);
    }

    public CircleProgressBar(Context paramContext, AttributeSet paramAttributeSet, int paramInt) {
        super(paramContext, paramAttributeSet, paramInt);
        this.DEFAULT_STROKE_WIDTH = 6.0F;
        this.mProgressRectF = new RectF(0.0F, 0.0F, 0.0F, 0.0F);
        this.mProgressBgRectF = new RectF(0.0F, 0.0F, 0.0F, 0.0F);
        this.mStrokeWith = 0.0F;
        this.unitAngle = 0.0F;
        this.context = paramContext;
        loadStyledAttr(paramContext, paramAttributeSet, paramInt);
        initPaint();
    }

    private void drawProgress(Canvas paramCanvas) {
        int i = getWidth() / 2;
        int j = i - Util.dip2px(this.context, 13.0F);
        paramCanvas.drawArc(new RectF(i - j, i - j, i + j, i + j), getStartAngle(), getSweepAngel(), getUseCenter(), this.mProgressPaint);
    }

    private void drawProgressBg(Canvas paramCanvas) {
        int i = getWidth() / 2;
        int j = i - Util.dip2px(this.context, 20.0F);
        paramCanvas.drawArc(new RectF(i - j, i - j, i + j, i + j), getStartAngle(), 360.0F, true, this.mProgressBgPaint);
    }

    private void drawProgressNum(Canvas paramCanvas) {
        this.mProgressBgRectF.left = 30.0F;
        this.mProgressBgRectF.top = 30.0F;
        this.mProgressBgRectF.right = 105.0F;
        this.mProgressBgRectF.bottom = 105.0F;
        Paint localPaint = new Paint(1);
        localPaint.setColor(Color.rgb(216, 76, 75));
        localPaint.setStyle(Paint.Style.STROKE);
        paramCanvas.drawArc(this.mProgressBgRectF, getStartAngle(), 360.0F, getUseCenter(), localPaint);
    }

    private int getStartAngle() {
        return -90;
    }

    private float getSweepAngel() {
        return (this.unitAngle * this.mProgress);
    }

    private boolean getUseCenter() {
        return false;
    }

    private void initPaint() {
        this.mPath = new Path();
        this.mProgressPaint = new Paint(1);
        this.mProgressPaint.setColor(this.mProgressColor);
        this.mProgressBgPaint = new Paint(1);
        this.mProgressBgPaint.setColor(this.mProgressBackgroundColor);
        this.mProgressBgPaint.setAntiAlias(true);
        this.mProgressPaint.setStyle(Paint.Style.STROKE);
        this.mProgressPaint.setStrokeJoin(Paint.Join.ROUND);
        this.mProgressPaint.setStrokeWidth(Util.dip2px(this.context, 10.0F));
        this.mProgressBgPaint.setStyle(Paint.Style.FILL);
        this.mProgressBgPaint.setStrokeWidth(this.mStrokeWith);
    }

    private void loadStyledAttr(Context paramContext, AttributeSet paramAttributeSet, int paramInt) {
        // 颜色设置
        this.mProgressBackgroundColor = DEFAULT_BACKGROUND_COLOR;
        this.mProgressColor = Color.parseColor("#ff48d502");
        this.mProgressEndColor = Color.parseColor("#ffe3b70a");
        this.mProgressEndColor2 = Color.parseColor("#ffcc0000");
        this.mMax = 100;
        this.mProgress = 0;
        this.DEFAULT_STROKE_WIDTH = Util.dip2px(getContext(), this.DEFAULT_STROKE_WIDTH);
        this.mStrokeWith = Util.dip2px(getContext(), this.DEFAULT_STROKE_WIDTH);
    }

    private int measure(int paramInt) {
        int i = View.MeasureSpec.getMode(paramInt);
        paramInt = View.MeasureSpec.getSize(paramInt);
        if (i == 1073741824)
            return paramInt;
        return Util.dip2px(getContext(), 80.0F);
    }

    private void onProgressChanged() {
        this.c = ((Integer) ColorUtils.evaluate((float) (this.mProgress / this.mMax * 1.5D), Integer.valueOf(this.mProgressColor), Integer.valueOf(this.mProgressEndColor))).intValue();
        this.mProgressPaint.setColor(this.c);
    }

    private void onProgressChangedEnd() {
        this.c = ((Integer) ColorUtils.evaluate((float) ((float) (this.mProgress / this.mMax - 0.75D) * 4.0D), Integer.valueOf(this.mProgressEndColor), Integer.valueOf(this.mProgressEndColor2))).intValue();
        this.mProgressPaint.setColor(this.c);
    }

    private void setUnitProgress() {
        this.unitAngle = (360.0F / this.mMax);
    }

    public float getMax() {
        return this.mMax;
    }

    public double getPrgoress() {
        return this.mProgress;
    }

    protected void onDraw(Canvas paramCanvas) {
        drawProgressBg(paramCanvas);
        drawProgress(paramCanvas);
    }

    protected void onMeasure(int paramInt1, int paramInt2) {
        setMeasuredDimension(measure(paramInt1), measure(paramInt2));
        setUnitProgress();
    }

    protected void onSizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) {
        this.l.sizeChanged(paramInt1, paramInt2, paramInt3, paramInt4);
        super.onSizeChanged(paramInt1, paramInt2, paramInt3, paramInt4);
    }

    public void setMax(float paramFloat) {
        this.mMax = paramFloat;
        setUnitProgress();
        invalidate();
    }

    public void setProgress(float paramFloat) {
        this.mProgress = paramFloat;
        if (paramFloat <= this.mMax * 0.75D)
            onProgressChanged();
        if (paramFloat > this.mMax * 0.75D)
            onProgressChangedEnd();
        invalidate();
    }

    public void setProgressWithAnim(int paramInt1, int paramInt2, long paramLong) {
        ValueAnimator localValueAnimator = ValueAnimator.ofInt(new int[]{paramInt1, paramInt2});
        localValueAnimator.setDuration(paramLong);
        localValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            public void onAnimationUpdate(ValueAnimator paramValueAnimator) {
            }
        });
        localValueAnimator.setInterpolator(new LinearInterpolator());
        localValueAnimator.start();
    }

    public void setProgressWithAnim(int paramInt, long paramLong) {
        setProgressWithAnim(0, paramInt, paramLong);
    }

    /**
     * 这里可用getViewTreeObserver()去做监听,消除耦合
     *
     * @param paramSizeChangeListener
     */
    public void setSizeChangeListener(SizeChangeListener paramSizeChangeListener) {
        this.l = paramSizeChangeListener;
    }


    public interface SizeChangeListener {
        void sizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4);
    }
}

源码有点长,但是里面画的东西却很少

 protected void onDraw(Canvas paramCanvas) {
        drawProgressBg(paramCanvas);
        drawProgress(paramCanvas);
    }

然后看下TestActivity01:

package com.example.tangxb.myapplication;

import android.graphics.Color;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.TextView;

/**
 * Created by Tangxb on 2016/4/19.
 */
public class TestActivity01 extends AppCompatActivity {
    private int circleRadius;
    private int circleX;
    private int circleY;
    private int circleY_init;
    private int duration = 1200;
    private float count = 0.0F;
    private float max;
    private MyProgressCount progressCount;
    private CircleProgressBar progress_bar;
    /**
     * 宽高为20dp的TextView
     */
    private TextView round_text;
    // 第一个效果表现没有第二个好
    Animation scaleAnimation1 = new ScaleAnimation(1.0F, 0.4F, 1.0F, 0.4F, Animation.RELATIVE_TO_SELF, 0.3F, Animation.RELATIVE_TO_SELF, 0.2F);
    // 使用第二个替代了第一个
    Animation scaleAnimation = new ScaleAnimation(1.0F, 0.0F, 1.0F, 0.0F, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
    private int window_width;
    private int progressBar_width;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.acttivity_test);
        init();
    }

    private void init() {
        scaleAnimation.setDuration(this.duration);
        max = 30000.0F;
        circleY_init = Util.dip2px(this, 85.0F);
        window_width = getWindowManager().getDefaultDisplay().getWidth();
        progress_bar = ((CircleProgressBar) findViewById(R.id.cpb));
        round_text = ((TextView) findViewById(R.id.tv));
        progress_bar.setMax(max);
        progress_bar.setSizeChangeListener(new CircleProgressBar.SizeChangeListener() {
            public void sizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) {
                progressBar_width = progress_bar.getWidth();
                if (progressCount == null) {
                    progressCount = new MyProgressCount();
                }
                progressCount.start();
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (progressBar_width != 0) {
            if (progressCount == null) {
                progressCount = new MyProgressCount();
            }
            progressCount.start();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (progressCount != null) {
            progressCount.cancel();
            progressCount = null;
        }
    }

    class MyProgressCount extends CountDownTimer {
        public MyProgressCount() {
            super(30000L, 25L);
        }

        public void onFinish() {
            start();
        }

        public void onTick(long paramLong) {
            count = (max - (float) paramLong);
            // 设置进度
            progress_bar.setProgress(count);
            circleRadius = (progressBar_width / 2);
            circleX = (window_width / 2 - circleRadius);
            circleY = (circleY_init + circleRadius);
            if (count >= max - 200.0F) {
            }
            if (count > max * 5.0F / 6.0F) {
                float f = (float) (count / 1000.0D);
                // 计算弧度值(sin需要的是弧度值不是角度值),至于是怎么得到的,数学渣渣表示懵逼
                double d = Math.PI * 12.0F * f / 180.0D;
                int i = (int) (Math.sin(d) * progress_bar.getWidth() / 2.0D);
                int j = (int) (Math.cos(d) * progress_bar.getWidth() / 2.0D);
                // 这里请注意里面使用的FrameLayout.LayoutParams,所以容器请用FrameLayout
                Util.setLayout(round_text, circleX + circleRadius - Math.abs(i), circleY - Math.abs(j));
                i = ((Integer) ColorUtils.evaluate((float) ((count / max - 0.75D) * 4.0D), Integer.valueOf(-1853686), Integer.valueOf(-3407872))).intValue();
                round_text.setBackgroundDrawable(Util.getRoundBg(i, 100));
                round_text.setTextColor(Color.WHITE);
                if ((f >= 25.0F) && (f <= 30.0F)) {
                    round_text.setVisibility(View.VISIBLE);
                    round_text.setText((int) (31.0F - f) + "");
                    if ((f >= 29.0F) && (f < 30.0F) && (!(scaleAnimation.hasStarted()))) {
                        round_text.setAnimation(scaleAnimation);
                        scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
                            public void onAnimationEnd(Animation paramAnimation) {
                                scaleAnimation = new ScaleAnimation(1.0F, 0.0F, 1.0F, 0.0F, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
                                scaleAnimation.setDuration(duration);
                                // 设置数字的text为GONE状态
                                round_text.setVisibility(View.GONE);
                            }

                            public void onAnimationRepeat(Animation paramAnimation) {
                            }

                            public void onAnimationStart(Animation paramAnimation) {
                                // 设置数字的text为VISIBLE状态
                                round_text.setVisibility(View.VISIBLE);
                            }
                        });
                        scaleAnimation.start();
                    }
                }
            }
        }
    }
}

附上ColorUtilsUtil以及xml文件

package com.example.tangxb.myapplication;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

/**
 * Created by Tangxb on 2016/7/25.
 */
public class Util {
    public static int dip2px(Context paramContext, float paramFloat) {
        return (int) (paramFloat * paramContext.getResources().getDisplayMetrics().density + 0.5F);
    }

    public static void setLayout(View paramView, int paramInt1, int paramInt2) {
        ViewGroup.MarginLayoutParams localMarginLayoutParams = new ViewGroup.MarginLayoutParams(paramView.getLayoutParams());
        localMarginLayoutParams.setMargins(paramInt1 - 14, paramInt2 + 10, localMarginLayoutParams.width + paramInt1, localMarginLayoutParams.height + paramInt2);
        paramView.setLayoutParams(new FrameLayout.LayoutParams(localMarginLayoutParams));
    }

    public static Drawable getRoundBg(int paramInt1, int paramInt2) {
        ShapeDrawable localShapeDrawable = new ShapeDrawable(new RoundRectShape(new float[]{paramInt2, paramInt2, paramInt2, paramInt2, paramInt2, paramInt2, paramInt2, paramInt2}, null, null));
        paramInt2 = paramInt1;
        if (paramInt1 == -1)
            paramInt2 = -1;
        localShapeDrawable.getPaint().setColor(paramInt2);
        return localShapeDrawable;
    }

}
package com.example.tangxb.myapplication;

/**
 * Created by Tangxb on 2016/7/25.
 */
public class ColorUtils {
    public static Object evaluate(float paramFloat, Object paramObject1, Object paramObject2)
    {
        int l = ((Integer)paramObject1).intValue();
        int i = l >> 24 & 0xFF;
        int j = l >> 16 & 0xFF;
        int k = l >> 8 & 0xFF;
        l &= 255;
        int i1 = ((Integer)paramObject2).intValue();
        return Integer.valueOf((int)(((i1 >> 24 & 0xFF) - i) * paramFloat) + i << 24 | (int)(((i1 >> 16 & 0xFF) - j) * paramFloat) + j << 16 | (int)(((i1 >> 8 & 0xFF) - k) * paramFloat) + k << 8 | (int)(((i1 & 0xFF) - l) * paramFloat) + l);
    }

    public static Object evaluate2(float paramFloat, Object paramObject1, Object paramObject2, Object paramObject3)
    {
        int l = ((Integer)paramObject1).intValue();
        int i = l >> 24 & 0xFF;
        int j = l >> 16 & 0xFF;
        int k = l >> 8 & 0xFF;
        l &= 255;
        int i1 = ((Integer)paramObject2).intValue();
        int i2 = ((Integer)paramObject3).intValue();
        return Integer.valueOf((int)(((i1 >> 24 & 0xFF) - i - (i2 >> 24 & 0xFF)) * paramFloat) + i << 24 | (int)(((i1 >> 16 & 0xFF) - j - (i2 >> 16 & 0xFF)) * paramFloat) + j << 16 | (int)(((i1 >> 8 & 0xFF) - k - (i2 >> 8 & 0xFF)) * paramFloat) + k << 8 | (int)(((i1 & 0xFF) - l - (i2 & 0xFF)) * paramFloat) + l);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="385.0dp">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="300.0dp"
            android:layout_marginTop="85.0dp">

            <com.example.tangxb.myapplication.CircleProgressBar
                android:id="@+id/cpb"
                android:layout_width="300.0dp"
                android:layout_height="300.0dp"
                android:layout_centerHorizontal="true" />
        </RelativeLayout>

        <TextView
            android:id="@+id/tv"
            android:layout_width="20.0dp"
            android:layout_height="20.0dp"
            android:gravity="center"
            android:textColor="#ff494949" />
    </FrameLayout>

</RelativeLayout>

我把里面能够自己还原的部分还原了,然而这里为了偷懒,就没有再用自定义属性去设置颜色值了。

一个不是那么优美的圆形进度条续(基本还原原应用里面的效果)

标签:

原文地址:http://blog.csdn.net/t1623183652/article/details/52022683

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!