自定义View——闹钟

举报
yd_221104950 发表于 2020/12/02 23:28:06 2020/12/02
【摘要】 我们今天来自定义一个闹钟。效果如下: 具体代码请参考:Demo 第一步:自定义属性 在文件app/src/main/res/values/attrs.xml中加入自定义属性: <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ClockView"> <attr...

我们今天来自定义一个闹钟。效果如下:
在这里插入图片描述

具体代码请参考:Demo

第一步:自定义属性

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

<?xml version="1.0" encoding="utf-8"?>
<resources> <declare-styleable name="ClockView"> <attr name="circle_color" format="color"/> <attr name="circle_scale_color" format="color"/> <attr name="sec_point_color" format="color"/> <attr name="min_point_color" format="color"/> <attr name="hour_point_color" format="color"/> <attr name="dot_color" format="color"/> </declare-styleable> ...
</resources>

  
 
  • 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
  • 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

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

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

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.wong.clock.ClockView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:background="@color/colorPrimaryDark" app:circle_color="@color/colorPrimaryDark" app:circle_scale_color="@android:color/white" app:dot_color="@android:color/white" app:hour_point_color="@android:color/white" app:min_point_color="@android:color/white" app:sec_point_color="@android:color/holo_red_dark" app:sec_point_width="3dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

  
 
  • 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

效果如前所示。

谢谢阅读!

文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_40763897/article/details/104007956

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。