天天看點

DialogFragment實作帶數字倒計時和小圓點進度的圓形進度條DialogFragment實作帶數字倒計時和小圓點進度的圓形進度條

DialogFragment實作帶數字倒計時和小圓點進度的圓形進度條

最近公司項目需要添加一個在網絡不理想時,支付等待倒計時的彈框。開始想通過自定義view畫出來,結果因為多個控件都有動畫效果宣告失敗。 最後利用繼承Drawable再加上動畫效果得以實作,效果如下:

DialogFragment實作帶數字倒計時和小圓點進度的圓形進度條DialogFragment實作帶數字倒計時和小圓點進度的圓形進度條

實作步驟

沒有互動的自定義view,可以實作一個drawable作為Imageview的背景

view的構成:

  • 外圍圓環進度條
  • 圓環内倒計時數字
  • 圓環下方文字
  • 文字右邊小圓點進度條

1. 自定義Drawable并初始化變量

public class CountDownView extends Drawable {
    private static final String TAG = "CountDownView";
    private final static int PROGRESS_FACTOR = -;
    private Paint mPaint;
    private Paint textPaint;
    private RectF mArcRect;
    private float radius;

    //目前進度條進度
    private float progress;
    //進度條顔色
    private int ringColor;
    //進度條寬度
    private int ringWidth;
    //倒計時數字
    private int showNumber;
    //字元串顔色
    private int textColor;
    //數字顔色
    private int numColor;

    private String showText = "正在支付";

    private int MAX_DOTS_COUNT = ;

    Paint numPaint;


    //小圓點數量
    private int circularCount;

    private Paint dotPaint;

    private static final float PERCENT_CIRCLER_TO_HEIGHT =  / f;//半徑占父控件高度的比例

    public CountDownView(int ringWidth, int ringColor, int showNumber, int textColor, int numColor) {
        mPaint = new Paint();
        numPaint = new Paint();
        mArcRect = new RectF();

        this.ringWidth = ringWidth;
        this.ringColor = ringColor;
        this.showNumber = showNumber;
        this.textColor = textColor;
        this.numColor = numColor;
    }

        @Override
        public void setAlpha(int alpha) {
            mPaint.setAlpha(alpha);
        }

        @Override
        public void setColorFilter(ColorFilter colorFilter) {

        }

        @Override
        public int getOpacity() {
            return mPaint.getAlpha();
        }
  }
           

2.實作draw方法

畫圓環

/**
  - 畫圓環 *
  - @param bounds
  - @param canvas */
  private void drawRing(Rect bounds, Canvas canvas) {

     int size = bounds.height() > bounds.width() ? bounds.width() : bounds.height();

     radius = size * PERCENT_CIRCLER_TO_HEIGHT;

     mPaint.setColor(ringColor); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(ringWidth);

     float cirX = bounds.centerX(); float cirY = bounds.centerY() * ( / f);

     canvas.translate(cirX, cirY);

     mArcRect.set(-radius, -radius, radius, radius); canvas.drawArc(mArcRect, -, progress, false, mPaint); }
           

畫倒計時數字

/**
    * 畫倒計時數字
    *
    * @param canvas
    */
   private void drawNum(Canvas canvas) {
       float textSize = radius * f * f;
       mPaint.setTextSize(textSize);
       mPaint.setTextAlign(Paint.Align.CENTER);
       mPaint.setColor(textColor);
       mPaint.setStrokeWidth(ringWidth / );
       mPaint.setStyle(Paint.Style.FILL);
       float numX = ;
       float numY = -(mPaint.descent() + mPaint.ascent()) / ;
       canvas.drawText(Integer.toString(showNumber), numX, numY, mPaint);
   }
`
           

畫文字

/**
    * 畫字元串
    *
    * @param canvas
    */
   private void drawText(Rect bounds, Canvas canvas) {
       textPaint = new Paint();
       textPaint.setAntiAlias(true);
       textPaint.setColor(textColor);
       textPaint.setTextAlign(Paint.Align.CENTER);
       textPaint.setStyle(Paint.Style.FILL);
       textPaint.setStrokeWidth(ringWidth / );
       float textSize = radius * f;
       textPaint.setTextSize(textSize);
       float textX = ;
       float textY = radius * f - (textPaint.descent() + textPaint.ascent()) / ;
       canvas.drawText(showText, textX, textY, textPaint);
   }
           

畫小圓點

/**
     * 畫小圓點
     *
     * @param canvas
     */
    private void drawDots(Canvas canvas) {
        Rect textBound = new Rect();
        dotPaint = new Paint();
        dotPaint.setAntiAlias(true);
        dotPaint.setColor(textColor);
        dotPaint.setStyle(Paint.Style.FILL);
        dotPaint.setStrokeWidth(ringWidth / );

        textPaint.getTextBounds(showText, , showText.length(), textBound);
        float dotWidth = textBound.width() / showText.length();

        /**
         * 三個圓點占用寬度為一個字元所占寬度,設定每個圓點間隔為直徑,第一個間距為一個半徑,是以半徑的計算方法為
         * 半徑 = 一個字元寬度 / ((2*圓點個數-1)+1)
         */

        float cirRadius = dotWidth / ((f * MAX_DOTS_COUNT - f) * f + f);
        float dotX = textBound.width() / ;
        float dotY = radius * f - (textPaint.descent() + textPaint.ascent()) * f;
//        Log.d(TAG, "dotX= " + dotX + "\n" + "dotY=" + dotY);
        for (int i = ; i <  * circularCount - ; i += ) {
            canvas.drawCircle(dotX + cirRadius * (i + ), dotY, cirRadius, dotPaint);
        }
    }
           

實作draw方法(調用以上畫控件的方法即可)

@Override
   public void draw(Canvas canvas) {
       final Rect bounds = getBounds();
       drawRing(bounds, canvas);

       drawNum(canvas);
       drawText(bounds, canvas);
       drawDots(canvas);
   }
           

3. 設定進度條以及倒計時數字和小圓點個數

public int getCircularCount() {
        return circularCount;
    }

    public void setCircularCount(int circularCount) {
        this.circularCount = circularCount;
        invalidateSelf();
    }

    public int getShowNumber() {
        return showNumber;
    }

    public void setShowNumber(int showNumber) {
        this.showNumber = showNumber;
        invalidateSelf();
    }

    public float getProgress() {
        return progress / PROGRESS_FACTOR;
    }

    public void setProgress(float progress) {
        this.progress = progress * PROGRESS_FACTOR;
        invalidateSelf();

    }
           

4.在DialogFragment中使用drawable

實作DialogFragment,并将countdownview設定為dialogfragment上一個imageview的背景圖檔

public class CountDownDialogFragment extends DialogFragment {

    private View rootView;
    ImageView countDown;

    private CountDownView mCdDrawable;
    private Animator mAnimator;
    CountDownDialogFragment dialog;
    private Window window;
    float width;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.count_down_dialog_frg, container, false);
        countDown = (ImageView) rootView.findViewById(R.id.count_down_iv);
        DisplayMetrics dm = getResources().getDisplayMetrics();
        width = dm.widthPixels;


        window = getDialog().getWindow();
        //背景透明
        window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        //去掉标題
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        int whites = getResources().getColor(R.color.white);
        mCdDrawable = new CountDownView(, whites, , whites, whites);
        countDown.setImageDrawable(mCdDrawable);

        if (mAnimator != null) {
            mAnimator.cancel();
        }
        countDown.setVisibility(View.VISIBLE);
        mAnimator = prepareAnimator();
        mAnimator.start();
        return rootView;
    }
           

使用屬性動畫,計算進度條progress以及倒計時數字和小圓點的變化規律

private Animator prepareAnimator() {
        AnimatorSet animation = new AnimatorSet();

        //進度條動畫
        ObjectAnimator progressAnimator = ObjectAnimator.ofFloat(mCdDrawable, "progress", f, f);
        progressAnimator.setDuration();
        progressAnimator.setInterpolator(new LinearInterpolator());
        progressAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                countDown.setVisibility(View.GONE);
                if (dialog != null)
                    dialog.dismiss();
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                countDown.setVisibility(View.GONE);
                if (dialog != null)
                    dialog.dismiss();
            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });

        // 居中的倒計時數字
        ObjectAnimator showNumAnimator = ObjectAnimator.ofInt(mCdDrawable, "showNumber", , );
        showNumAnimator.setDuration();
        showNumAnimator.setInterpolator(new LinearInterpolator());

        //小圓點進度條
        ObjectAnimator dotProgressAnimator = ObjectAnimator.ofInt(mCdDrawable, "circularCount", , );
        dotProgressAnimator.setDuration();
        dotProgressAnimator.setRepeatCount(ValueAnimator.INFINITE);
        dotProgressAnimator.setRepeatMode(ValueAnimator.RESTART);
        dotProgressAnimator.setInterpolator(new LinearInterpolator());

        animation.playTogether(progressAnimator, showNumAnimator, dotProgressAnimator);
        return animation;

    }
           

修改DialogFragment窗體大小,需要在onResume方法中實作

@Override
   public void onResume() {
       super.onResume();
       dialog = this;
       //設定大小
       window.setLayout((int) (width * f), (int) (width * f * ( / f)));
   }
           

源碼請到Github

繼續閱讀