Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 一個不是那麼優美的圓形進度條續(基本還原原應用裡面的效果)

一個不是那麼優美的圓形進度條續(基本還原原應用裡面的效果)

編輯:關於Android編程

之前幫別人寫了一個不是那麼優美的圓形進度條,效果圖大家也看過了。但是後某人不滿意,說原應用是倒計時時間最後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個類,工具類:Util和ColorUtils;界面類:TestActivity01和CircleProgressBar。
先看下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();
                    }
                }
            }
        }
    }
}

附上ColorUtils和Util以及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);
    }
}



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

        

            
        

        
    </framelayout>


我把裡面能夠自己還原的部分還原了,然而這裡為了偷懶,就沒有再用自定義屬性去設置顏色值了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved