效果图预览
gif图片录制的不是很清晰
1. 分析和技术实现
1. 绘制一个外圆和一个红色的内圆,还需要绘制中间的一个小圆点
2. 绘制刻度,这里我才用了旋转画布的操作来简化计算
3. 主要就是canvas的操作,canvas.save和canvas.restore成对出现,保证当前画布操作不会影响其他的
4. 因为秒钟要每秒更新一次,需要调用postInvalidateDelayed(1000)一秒钟发一次延迟消息 刷新控件
2. 初始化一些东西
//外面圆的半径
private int mOutCircleRadius;
//钟表的半径
private int mClockRadius;
//中间小圆点半径
private int mCenterRadius;
//圆心坐标
private int mCenterX;
private int mCenterY;
//时钟刻度线的高度
private int mScaleLineHeight;
//画笔对象
private Paint mClockPaint;
private Paint mCirclePaint;
private Paint mTimePaint;
//时间操作辅助类
private Calendar mCalendar;
//刻度线数量 钟表要分多少等分 一般是12根线
private static final int SCALE_NUM = 12;
//外圆和钟表的边的宽度
private int mOutCircleStrokeWidth;
private int mClockStrokeWidth;
//初始化各种颜色值
private int mClockColor;
private int mCircleColor;
private int mHourColor;
private int mMinuteColor;
private int mSecondColor;
private int mScaleColor;
private int mMidCircleColor;
3. 自定义属性值
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ClockView">
<attr name="hour_line_color" format="color"/>
<attr name="minute_line_color" format="color"/>
<attr name="second_line_color" format="color"/>
<attr name="scale_line_color" format="color"/>
<attr name="clock_color" format="color"/>
<attr name="out_circle_color" format="color"/>
<attr name="mid_circle_color" format="color"/>
</declare-styleable>
</resources>
代码中取值
private void initAttrs(AttributeSet attrs) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs,R.styleable.ClockView);
mClockColor = typedArray.getColor(R.styleable.ClockView_clock_color, Color.RED);
mCircleColor = typedArray.getColor(R.styleable.ClockView_out_circle_color, Color.GRAY);
mHourColor = typedArray.getColor(R.styleable.ClockView_hour_line_color, Color.RED);
mMinuteColor = typedArray.getColor(R.styleable.ClockView_minute_line_color, Color.BLUE);
mSecondColor = typedArray.getColor(R.styleable.ClockView_second_line_color, Color.GREEN);
mScaleColor = typedArray.getColor(R.styleable.ClockView_scale_line_color, Color.RED);
mMidCircleColor = typedArray.getColor(R.styleable.ClockView_mid_circle_color, Color.RED);
//记得回收 不然有可能造成内存泄漏
typedArray.recycle();
}
4.onMeasure和onSizeChanged处理
onMeasure主要是处理wrap_content的情况,如果宽高为
wrap_content的情况下我给默认指定了大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//当宽度为wrap_content时我们给一个默认值
int width = dp2px(140);
int height = dp2px(140);
width = widthMode == MeasureSpec.AT_MOST ? width : widthSize;
height = heightMode == MeasureSpec.AT_MOST ? height : heightSize;
setMeasuredDimension(width,height);
}
//根据控件宽高计算外圆半径和钟表的半径
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mCenterX = w / 2;
mCenterY = h / 2;
//外圆半径 = 控件宽度的一半 - 外圆边的宽度
mOutCircleRadius = w / 2 - mOutCircleStrokeWidth;
//钟表半径 = 控件宽度的一半 - 外圆边的宽度 - 钟表边的宽度
mClockRadius = w / 2 - mOutCircleStrokeWidth - mClockStrokeWidth;
mCenterRadius = dp2px(3);
mScaleLineHeight = dp2px(6);
}
5. 绘制外圆,钟表圆和中心点
//绘制圆
private void drawClockCircle(Canvas canvas) {
canvas.save();
//画钟表的圆
mClockPaint.setColor(mClockColor);
mClockPaint.setStrokeWidth(mClockStrokeWidth);
canvas.drawCircle(0,0, mClockRadius, mClockPaint);
//画钟表的外圆
mCirclePaint.setStyle(Paint.Style.STROKE);
mCirclePaint.setColor(mCircleColor);
canvas.drawCircle(0,0, mOutCircleRadius, mCirclePaint);
//画钟表的中心圆点
mCirclePaint.setStyle(Paint.Style.FILL);
mCirclePaint.setColor(mMidCircleColor);
canvas.drawCircle(0,0, mCenterRadius,mCirclePaint);
canvas.restore();
}
6. 绘制钟表的刻度
钟表的每一根刻度线理论上都需要计算它的角度,然后计算它的x,y坐标值,但是计算量很大,我投机取巧,通过旋转画布的方式来简化绘制过程,比如我要绘制2点钟的刻度线 我顺时针旋转2 * 30 = 60就可以了, 从12点位置开始绘制
//根据刻度线的数量来获取旋转画布的角度
float angle = 360 / SCALE_NUM;
for (int i = 0; i < SCALE_NUM; i++) {
canvas.save();
canvas.rotate(i * angle);
//从12点位置开始绘制
canvas.drawLine(0, - mClockRadius + lineSpacing, 0, - mClockRadius +
mScaleLineHeight + lineSpacing,mClockPaint);
canvas.restore();
}
//绘制钟表的刻度
private void drawClockScale(Canvas canvas) {
mClockPaint.setStrokeWidth(dp2px(3));
mClockPaint.setColor(mScaleColor);
//刻度线与钟表的间距
int lineSpacing = dp2px(6);
//通过旋转画布的方式来简化绘制过程,不然得计算每一根刻度线的角度和x,y坐标值
//比如我要绘制2点钟的刻度线 我顺时针旋转2 * 30 = 60就可以了
//根据刻度线的数量来获取旋转画布的角度
float angle = 360 / SCALE_NUM;
for (int i = 0; i < SCALE_NUM; i++) {
canvas.save();
canvas.rotate(i * angle);
//从12点位置开始绘制
canvas.drawLine(0, - mClockRadius + lineSpacing, 0, - mClockRadius +
mScaleLineHeight + lineSpacing,mClockPaint);
canvas.restore();
}
}
7. 绘制小时,分钟和秒钟的线
由Calendar.getInstance()获取Calendar对象
int hour = mCalendar.get(Calendar.HOUR);
int minute = mCalendar.get(Calendar.MINUTE);
int second = mCalendar.get(Calendar.SECOND);
分别获取当前的小时,分钟和秒的数值,然后根据当前的时分秒选中相应的角度,画线就这么简单
//一秒钟发一次延迟消息 刷新控件
postInvalidateDelayed(1000);
//绘制小时,分钟,秒的线
private void drawTimeScaleLine(Canvas canvas) {
mCalendar = Calendar.getInstance();
//绘制小时的刻度线
canvas.save();
mTimePaint.setStrokeWidth(dp2px(3));
mTimePaint.setColor(mHourColor);
int hour = mCalendar.get(Calendar.HOUR);
//360 / 12小时 = 30度
canvas.rotate(hour * 30f);
//基于12点位置画线 所以x坐标就是圆心的x坐标 坐标系向下为正方向 所以y为负值
canvas.drawLine(0,-dp2px(6),0, - mClockRadius * 0.50f,mTimePaint);
canvas.restore();
//绘制分钟的刻度线
canvas.save();
mTimePaint.setStrokeWidth(dp2px(2));
mTimePaint.setColor(mMinuteColor);
int minute = mCalendar.get(Calendar.MINUTE);
//360 / 60分钟 = 6度
canvas.rotate(minute * 6f);
canvas.drawLine(0,-dp2px(6),0,- mClockRadius * 0.62f,mTimePaint);
canvas.restore();
//绘制秒钟的刻度线
canvas.save();
mTimePaint.setStrokeWidth(dp2px(1.5f));
mTimePaint.setColor(mSecondColor);
int second = mCalendar.get(Calendar.SECOND);
Log.e("ClockView second",second+"");
// 360 / 60秒 = 6度
canvas.rotate(second * 6f);
canvas.drawLine(0,-dp2px(6),0,- mClockRadius * 0.74f,mTimePaint);
canvas.restore();
//一秒钟发一次延迟消息 刷新控件
postInvalidateDelayed(1000);
}
8.源代码下载
最后统一提供下载地址
9.联系方式
QQ:1509815887