自定义View——闹钟

网友投稿 513 2022-05-29

我们今天来自定义一个闹钟。效果如下:

具体代码请参考:Demo

第一步:自定义属性

在文件app/src/main/res/values/attrs.xml中加入自定义属性:

...

1

2

3

4

5

6

7

8

9

10

11

12

第二步:重写onMeasure 、onLayout、onDraw方法

重写onMeasure主要是为了解决,当wrap_content时,视图会占满整个父布局,所以要设置一个默认值。

重写onLayout是因为遵守良好的习惯。因为onMeasure可能会重复调用多次,那么视图的宽高就可能在最后一次才能确定下来。当然这种情况更可能出现在继承ViewGroup中的自定义视图中。我们本例是继承View,所以一般不会出现上述情况。onLayout中一定能拿到视图的宽高而且是确定下来的。

onDraw方法一般都需要重写。

package com.wong.clock; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.Calendar; public class ClockView extends View { /*画笔*/ private Paint mPaintCircle = new Paint(); private Paint mPaintDot = new Paint(); private Paint mPaintScale = new Paint(); private Paint mPaintHour = new Paint(); private Paint mPaintMin = new Paint(); private Paint mPaintSec = new Paint(); private int width = 0; private int height = 0; private int circleColor = Color.WHITE; private int scaleColor = Color.GRAY; private int dotColor = Color.BLACK; private int hourPointColor = Color.BLACK; private int minPointColor = Color.BLACK; private int secPointColor = Color.RED; private float hourPointWidth = 12f; private float minPointWidth = 8f; private float secPointWidth = 2f; private float dotWidth = 0; public void setCircleColor(int circleColor){ this.circleColor = circleColor; } public void setDotColor(int dotColor) { this.dotColor = dotColor; } public void setHourPointColor(int hourPointColor) { this.hourPointColor = hourPointColor; } public void setMinPointColor(int minPointColor) { this.minPointColor = minPointColor; } public void setSecPointColor(int secPointColor) { this.secPointColor = secPointColor; } public void setHourPointWidth(float hourPointWidth) { this.hourPointWidth = hourPointWidth; } public void setMinPointWidth(float minPointWidth) { this.minPointWidth = minPointWidth; } public void setSecPointWidth(float secPointWidth) { this.secPointWidth = secPointWidth; } public ClockView(Context context) { super(context); init(context,null); } public ClockView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context,attrs); } public ClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context,attrs); } public ClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context,attrs); } private void init(@NonNull Context context,AttributeSet attrs){ /*初始化xml属性的值*/ if(attrs != null) { TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.ClockView); circleColor = typedArray.getColor(R.styleable.ClockView_circle_color,circleColor); scaleColor = typedArray.getColor(R.styleable.ClockView_circle_scale_color,scaleColor); dotColor = typedArray.getColor(R.styleable.ClockView_dot_color,dotColor); hourPointColor = typedArray.getColor(R.styleable.ClockView_hour_point_color,hourPointColor); minPointColor = typedArray.getColor(R.styleable.ClockView_min_point_color,minPointColor); secPointColor = typedArray.getColor(R.styleable.ClockView_sec_point_color,secPointColor); hourPointWidth = typedArray.getDimension(R.styleable.ClockView_hour_point_width,hourPointWidth); minPointWidth = typedArray.getDimension(R.styleable.ClockView_min_point_width,minPointWidth); secPointWidth = typedArray.getDimension(R.styleable.ClockView_sec_point_width,secPointWidth); dotWidth = typedArray.getDimension(R.styleable.ClockView_dot_width,dotWidth); typedArray.recycle(); } /*表盘*/ mPaintCircle.setStyle(Paint.Style.FILL); mPaintCircle.setAntiAlias(true); mPaintCircle.setColor(circleColor); /*刻度*/ mPaintScale.setStyle(Paint.Style.FILL); mPaintScale.setAntiAlias(true); mPaintScale.setColor(scaleColor); /*圆心的圆*/ mPaintDot.setStyle(Paint.Style.FILL); mPaintDot.setAntiAlias(true); mPaintDot.setColor(dotColor); /*hour*/ mPaintHour.setAntiAlias(true); mPaintHour.setStrokeWidth(hourPointWidth); mPaintHour.setStyle(Paint.Style.FILL); mPaintHour.setColor(hourPointColor); /*min*/ mPaintMin.setAntiAlias(true); mPaintMin.setStrokeWidth(minPointWidth); mPaintMin.setStyle(Paint.Style.FILL); mPaintMin.setColor(minPointColor); /*sec*/ mPaintSec.setAntiAlias(true); mPaintSec.setStrokeWidth(secPointWidth); mPaintSec.setStyle(Paint.Style.FILL); mPaintSec.setColor(secPointColor); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int widthW = widthSize - getPaddingLeft()-getPaddingRight(); int heightH = heightSize - getPaddingTop() - getPaddingBottom(); switch (widthMode){ case MeasureSpec.EXACTLY: case MeasureSpec.AT_MOST: if(getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT) { width = widthW; }else if(getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT){ width = (int)getResources().getDimension(R.dimen.clock_view_width); }else{ width = widthW; } break; case MeasureSpec.UNSPECIFIED: width = widthW; break; } switch (heightMode){ case MeasureSpec.EXACTLY: case MeasureSpec.AT_MOST: if(getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT) { height = heightH; }else if(getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT){ height = (int)getResources().getDimension(R.dimen.clock_view_height); }else{ height = heightH; } break; case MeasureSpec.UNSPECIFIED: height = heightH; break; } setMeasuredDimension(width,height); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); width = getMeasuredWidth(); height = getMeasuredHeight(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /*设置画布的颜色*/ canvas.drawColor(Color.WHITE); int radius = Math.min(width,height)/2; /*画刻盘*/ canvas.drawCircle(width/2,height/2,radius,mPaintCircle); /*绘制12个点,代表12小时*/ float scaleRadius = radius / 24; float dotY = (float) height/2-radius+5*scaleRadius/4; for(int i = 0; i < 12; i++){ canvas.drawCircle(width/2, dotY, scaleRadius, mPaintScale); canvas.rotate(30f,width/2,height/2); } /*绘制时针、分针、秒针*/ Calendar calendar = Calendar.getInstance(); float hour = calendar.get(Calendar.HOUR); float min = calendar.get(Calendar.MINUTE); float sec = calendar.get(Calendar.SECOND); /*时针转过的角度*/ float angleHour = hour * (float) (360 / 12) + min/60*30; /*分针转过的角度*/ float angleMin = min * 6; /*秒针转过的角度*/ float angleSec = sec * 6; /*绘制时针*/ float hourY = (float) height/2-radius+(float) radius / 2; canvas.save(); canvas.rotate(angleHour,width/2,height/2); canvas.drawLine(width/2,height/2,width/2,hourY,mPaintHour); canvas.restore(); /*绘制分针*/ float minY = (float) height/2-radius+(float) radius / 3; canvas.save(); canvas.rotate(angleMin,width/2,height/2); canvas.drawLine(width/2,height/2,width/2,minY,mPaintMin); canvas.restore(); /*绘制分针*/ float secY = (float) height/2-radius+(float) radius / 5; canvas.save(); canvas.rotate(angleSec,width/2,height/2); canvas.drawLine(width/2,height/2,width/2,secY,mPaintSec); canvas.restore(); /*在圆心画个圆盖住三条指针的连接处*/ float dotRadius = Math.max(dotWidth,(float) 0.05*radius); canvas.drawCircle(width/2,height/2,dotRadius,mPaintDot); /*每隔1秒就刷新*/ postDelayed(new Runnable() { @Override public void run() { invalidate(); } },1000); } }

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

自定义View——闹钟

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

代码的具体含义,请看代码中的注释。

第三步:在布局文件中使用闹钟视图

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

效果如前所示。

谢谢阅读!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:手把手教你完成贪吃蛇的编写(Python)
下一篇:MySQL表加密是鸡肋吗?
相关文章