DialogFragment實作帶數字倒計時和小圓點進度的圓形進度條
最近公司項目需要添加一個在網絡不理想時,支付等待倒計時的彈框。開始想通過自定義view畫出來,結果因為多個控件都有動畫效果宣告失敗。 最後利用繼承Drawable再加上動畫效果得以實作,效果如下:
實作步驟
沒有互動的自定義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)));
}