服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - Android自定义圆环倒计时控件

Android自定义圆环倒计时控件

2022-11-01 16:11StarkSongV Android

这篇文章主要为大家详细介绍了Android自定义圆环倒计时控件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了Android自定义圆环倒计时控件的具体代码,供大家参考,具体内容如下

先来一张最终效果图:

Android自定义圆环倒计时控件

主要思路: 在画渐变色圆环的时候,设置一个属性动画,根据属性动画的执行时长,来作为倒计时的时长.监听属性动画的进度,来达到 倒计时的目的.

二话不说,直接贴代码.具体实现思路都在注释上.

自定义属性:

?
1
2
3
4
5
6
7
8
9
<declare-styleable name="CountDownProgressBar">
  <attr name="countDown_circleWidth" format="dimension" />
  <attr name="countDown_centerTextSize" format="dimension" />
  <attr name="countDown_betaAngle" format="integer" />
  <attr name="countDown_firstColor" format="color" />
  <attr name="countDown_secondColor" format="color" />
  <attr name="countDown_centerTextColor" format="color" />
  <attr name="countDown_isShowGradient" format="boolean" />
</declare-styleable>

主要代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.LinearInterpolator;
 
import com.daodaojk.myapplication.R;
 
public class CountDownProgressBar extends View {
 /**
  * 进度条最大值
  */
 private int maxValue = 200;
 
 /**
  * 当前进度值
  */
 private int currentValue ;
 
 /**
  * 每次扫过的角度,用来设置进度条圆弧所对应的圆心角,alphaAngle=(currentValue/maxValue)*360
  */
 private float alphaAngle;
 
 /**
  * 底部圆弧的颜色,默认为Color.LTGRAY
  */
 private int firstColor;
 
 /**
  * 进度条圆弧块的颜色
  */
 private int secondColor;
 /**
  * 中间文字颜色(默认蓝色)
  */
 private int centerTextColor = Color.BLUE;
 /**
  * 中间文字的字体大小(默认40dp)
  */
 private int centerTextSize;
 
 /**
  * 圆环的宽度
  */
 private int circleWidth;
 
 /**
  * 画圆弧的画笔
  */
 private Paint circlePaint;
 
 /**
  * 画文字的画笔
  */
 private Paint textPaint;
 /**
  * 是否使用渐变色
  */
 private boolean isShowGradient = false;
 
 /**
  * 渐变圆周颜色数组
  */
 private int[] colorArray = new int[]{Color.parseColor("#2773FF"),
   Color.parseColor("#27C0D2"), Color.parseColor("#40C66E")};
 private int duration;
 private OnFinishListener listener;
 private ValueAnimator animator;
 
 public CountDownProgressBar(Context context) {
  this(context, null);
 }
 
 
 public CountDownProgressBar(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }
 
 
 public CountDownProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
 
  TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CountDownProgressBar,
    defStyleAttr, 0);
  int n = ta.getIndexCount();
 
  for (int i = 0; i < n; i++) {
   int attr = ta.getIndex(i);
   switch (attr) {
    case R.styleable.CountDownProgressBar_countDown_firstColor:
     firstColor = ta.getColor(attr, Color.LTGRAY); // 默认底色为亮灰色
     break;
    case R.styleable.CountDownProgressBar_countDown_secondColor:
     secondColor = ta.getColor(attr, Color.BLUE); // 默认进度条颜色为蓝色
     break;
    case R.styleable.CountDownProgressBar_countDown_centerTextSize:
     centerTextSize = ta.getDimensionPixelSize(attr, (int) dip2px(40)); // 默认中间文字字体大小为40dp
     break;
    case R.styleable.CountDownProgressBar_countDown_circleWidth:
     circleWidth = ta.getDimensionPixelSize(attr, (int) dip2px(6f)); // 默认圆弧宽度为6dp
     break;
    case R.styleable.CountDownProgressBar_countDown_centerTextColor:
     centerTextColor = ta.getColor(attr, Color.BLUE); // 默认中间文字颜色为蓝色
     break;
    case R.styleable.CountDownProgressBar_countDown_isShowGradient:
     isShowGradient = ta.getBoolean(attr, false); // 默认不适用渐变色
     break;
    default:
     break;
   }
  }
  ta.recycle();
 
  circlePaint = new Paint();
  circlePaint.setAntiAlias(true); // 抗锯齿
  circlePaint.setDither(true); // 防抖动
  circlePaint.setStrokeWidth(circleWidth);//画笔宽度
 
  textPaint = new Paint();
  textPaint.setAntiAlias(true);
  textPaint.setDither(true);
 }
 
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  // 分别获取期望的宽度和高度,并取其中较小的尺寸作为该控件的宽和高,并且不超过屏幕宽高
  int widthPixels = this.getResources().getDisplayMetrics().widthPixels;//获取屏幕宽
  int heightPixels = this.getResources().getDisplayMetrics().heightPixels;//获取屏幕高
  int width = MeasureSpec.getSize(widthMeasureSpec);
  int hedight = MeasureSpec.getSize(heightMeasureSpec);
  int minWidth = Math.min(widthPixels, width);
  int minHedight = Math.min(heightPixels, hedight);
  setMeasuredDimension(Math.min(minWidth, minHedight), Math.min(minWidth, minHedight));
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
  int center = this.getWidth() / 2;
  int radius = center - circleWidth / 2;
 
  drawCircle(canvas, center, radius); // 绘制进度圆弧
  drawText(canvas, center);
 }
 
 /**
  * 绘制进度圆弧
  *
  * @param canvas 画布对象
  * @param center 圆心的x和y坐标
  * @param radius 圆的半径
  */
 private void drawCircle(Canvas canvas, int center, int radius) {
  circlePaint.setShader(null); // 清除上一次的shader
  circlePaint.setColor(firstColor); // 设置底部圆环的颜色,这里使用第一种颜色
  circlePaint.setStyle(Paint.Style.STROKE); // 设置绘制的圆为空心
  canvas.drawCircle(center, center, radius, circlePaint); // 画底部的空心圆
  RectF oval = new RectF(center - radius, center - radius, center + radius, center + radius); // 圆的外接正方形
  if (isShowGradient) {
   // 绘制颜色渐变圆环
   // shader类是Android在图形变换中非常重要的一个类。Shader在三维软件中我们称之为着色器,其作用是来给图像着色。
   LinearGradient linearGradient = new LinearGradient(circleWidth, circleWidth, getMeasuredWidth()
     - circleWidth, getMeasuredHeight() - circleWidth, colorArray, null, Shader.TileMode.MIRROR);
   circlePaint.setShader(linearGradient);
  }
  circlePaint.setShadowLayer(10, 10, 10, Color.BLUE);
  circlePaint.setColor(secondColor); // 设置圆弧的颜色
  circlePaint.setStrokeCap(Paint.Cap.ROUND); // 把每段圆弧改成圆角的
 
  alphaAngle = currentValue * 360.0f / maxValue * 1.0f; // 计算每次画圆弧时扫过的角度,这里计算要注意分母要转为float类型,否则alphaAngle永远为0
  canvas.drawArc(oval, -90, alphaAngle, false, circlePaint);
 }
 
 /**
  * 绘制文字
  *
  * @param canvas 画布对象
  * @param center 圆心的x和y坐标
  */
 private void drawText(Canvas canvas, int center) {
  int result = ((maxValue - currentValue) * (duration / 1000) / maxValue); // 计算进度
  String percent;
  if (maxValue == currentValue) {
   percent = "完成";
   textPaint.setTextSize(centerTextSize); // 设置要绘制的文字大小
  } else {
   percent = (result / 60 < 10 ? "0" + result / 60 : result / 60) + ":" + (result % 60 < 10 ? "0" + result % 60 : result % 60);
//   percent = result+"秒";
   textPaint.setTextSize(centerTextSize+centerTextSize/3); // 设置要绘制的文字大小
  }
  textPaint.setTextAlign(Paint.Align.CENTER); // 设置文字居中,文字的x坐标要注意
  textPaint.setColor(centerTextColor); // 设置文字颜色
 
  textPaint.setStrokeWidth(0); // 注意此处一定要重新设置宽度为0,否则绘制的文字会重叠
  Rect bounds = new Rect(); // 文字边框
  textPaint.getTextBounds(percent, 0, percent.length(), bounds); // 获得绘制文字的边界矩形
  FontMetricsInt fontMetrics = textPaint.getFontMetricsInt(); // 获取绘制Text时的四条线
  int baseline = center + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom; // 计算文字的基线  canvas.drawText(percent, center, baseline, textPaint); // 绘制表示进度的文字
 
 }
 
 /**
  * 设置圆环的宽度
  *
  * @param width
  */
 public void setCircleWidth(int width) {
  this.circleWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width, getResources()
    .getDisplayMetrics());
  circlePaint.setStrokeWidth(circleWidth);
  //一般只是希望在View发生改变时对UI进行重绘。invalidate()方法系统会自动调用 View的onDraw()方法。
  invalidate();
 }
 
 /**
  * 设置圆环的底色,默认为亮灰色LTGRAY
  *
  * @param color
  */
 public void setFirstColor(int color) {
  this.firstColor = color;
  circlePaint.setColor(firstColor);
  //一般只是希望在View发生改变时对UI进行重绘。invalidate()方法系统会自动调用 View的onDraw()方法。
  invalidate();
 }
 
 /**
  * 设置进度条的颜色,默认为蓝色<br>
  *
  * @param color
  */
 public void setSecondColor(int color) {
  this.secondColor = color;
  circlePaint.setColor(secondColor);
  //一般只是希望在View发生改变时对UI进行重绘。invalidate()方法系统会自动调用 View的onDraw()方法。
  invalidate();
 }
 
 /**
  * 设置进度条渐变色颜色数组
  *
  * @param colors 颜色数组,类型为int[]
  */
 public void setColorArray(int[] colors) {
  this.colorArray = colors;
  //一般只是希望在View发生改变时对UI进行重绘。invalidate()方法系统会自动调用 View的onDraw()方法。
  invalidate();
 }
 
 
 /**
  * 按进度显示百分比,可选择是否启用数字动画
  *
  * @param duration 动画时长
  */
 public void setDuration(int duration, OnFinishListener listener) {
   this.listener = listener;
  this.duration = duration + 1000;
  if (animator != null) {
   animator.cancel();
  } else {
   animator = ValueAnimator.ofInt(0, maxValue);
   animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
     currentValue = (int) animation.getAnimatedValue();
     //一般只是希望在View发生改变时对UI进行重绘。invalidate()方法系统会自动调用 View的onDraw()方法。
     invalidate();
     if (maxValue == currentValue && CountDownProgressBar.this.listener != null) {
      CountDownProgressBar.this.listener.onFinish();
     }
    }
   });
   animator.setInterpolator(new LinearInterpolator());
  }
  animator.setDuration(duration);
  animator.start();
 }
 
 public interface OnFinishListener {
  void onFinish();
 }
 
 public void setOnFinishListener(OnFinishListener listener) {
  this.listener = listener;
 }
 
 public static int px2dip(int pxValue) {
  final float scale = Resources.getSystem().getDisplayMetrics().density;
  return (int) (pxValue / scale + 0.5f);
 }
 
 
 public static float dip2px(float dipValue) {
  final float scale = Resources.getSystem().getDisplayMetrics().density;
  return (dipValue * scale + 0.5f);
 }
}

xml布局:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:orientation="vertical">
<Button
 android:layout_width="match_parent"
 android:text="开始"
 android:id="@+id/btn_start"
 android:layout_height="wrap_content" />
 <com.daodaojk.myapplication.view.CountDownProgressBar
  android:id="@+id/cpb_countdown"
  android:layout_width="200dp"
  android:layout_marginTop="100dp"
  android:layout_gravity="center_horizontal"
  app:countDown_centerTextSize="25dp"
  app:countDown_circleWidth="4dp"
  app:countDown_firstColor="@color/text_gray_ccc"
  app:countDown_secondColor="@color/text_blue"
  app:countDown_isShowGradient="true"
  app:countDown_centerTextColor="#2395FF"
  android:layout_height="200dp" />
</LinearLayout>

页面调用:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.daodaojk.myapplication.ui;
 
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
 
import com.daodaojk.myapplication.R;
import com.daodaojk.myapplication.view.CountDownProgressBar;
 
public class CountDownActivity extends AppCompatActivity {
 
 private CountDownProgressBar cpb_countdown;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_count_down);
  Button btn_start = findViewById(R.id.btn_start);
  cpb_countdown = (CountDownProgressBar) findViewById(R.id.cpb_countdown);
  btn_start.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    cpb_countdown.setDuration(10000, new CountDownProgressBar.OnFinishListener() {
     @Override
     public void onFinish() {
      Toast.makeText(CountDownActivity.this, "完成了", Toast.LENGTH_SHORT).show();
     }
    });
   }
  });
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/baidu_36619149/article/details/80163745

延伸 · 阅读

精彩推荐