一个很简单的自定义实例,直接上代码,注释已经写得很清楚了
attrs:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyClockView">
<attr name="wb_radius" format="dimension"/>
<!--背景颜色-->
<attr name="wb_background_color" format="color"/>
<!--0369点刻度颜色-->
<attr name="wb_scale_large_long_color" format="color"/>
<!--时刻刻度颜色-->
<attr name="wb_scale_long_color" format="color"/>
<!--非时刻刻度颜色-->
<attr name="wb_scale_short_color" format="color"/>
<!--时针颜色-->
<attr name="wb_hour_pointer_color" format="color"/>
<!--分针颜色-->
<attr name="wb_minute_pointer_color" format="color"/>
<!--秒针颜色-->
<attr name="wb_second_pointer_color" format="color"/>
<!--主题风格-->
<attr name="wb_type" format="integer"/>
</declare-styleable>
</resources>
MyClockView:
public class MyClockView extends View {
private float mRadius; //外圆半径
private int mBackground; //背景颜色
private int mColorLong; //长线的颜色
private int mColorShort; //短线的颜色
private int mColorLargeLong; //0369点刻度颜色
private int mHourPointColor; //时针的颜色
private int mMinutePointColor; //分针的颜色
private int mSecondPointColor; //秒针的颜色
private int mType; //风格类型
private Paint mPaint; //画笔
private Calendar calendar;
private float angleHour; //时针转过的角度
private int angleMinute, angleSecond; //分针和秒针转过的角度
private Bitmap background; //背景图片
private Rect backgroundRect; // 用于缩放背景图片的矩形
private float oneUnit; // 一个单位的长度,由半径得到
private final static int NORMAL = 1; //普通风格
private final static int FRESH = 2; //清新风格
public MyClockView(Context context) {
this(context, null);
}
public MyClockView(Context context, AttributeSet attrs) {
super(context, attrs);
initAttrs(context, attrs);
initPaint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 取计算的长度和宽度的最小值,保证view所占空间为正方形
int specSize = Math.min(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
setMeasuredDimension(specSize, specSize);
if (mRadius == - 1) //如果未设置半径,把半径设为view的长度的一半
mRadius = specSize / 2;
int intRadius = (int) mRadius;
backgroundRect = new Rect(-intRadius, -intRadius, intRadius, intRadius);
oneUnit = (float) (mRadius / 118.5);
}
@Override
protected void onDraw(Canvas canvas) {
calendar = Calendar.getInstance();
int hour = calendar.get(Calendar.HOUR); //时
int minute = calendar.get(Calendar.MINUTE); //分
int second = calendar.get(Calendar.SECOND); //秒
int milliSecond = calendar.get(Calendar.MILLISECOND);
angleHour = ((hour + (minute + second * 1.0f / 60) * 1.0f / 60)) * 360 / 12; //时针转过的角度
angleMinute = minute * 360 / 60; //分针转过的角度
angleSecond = second * 360 / 60; //秒针转过的角度
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
// 根据设置的风格进行绘制
if (mType == NORMAL) {
drawNormal(canvas);
} else if (mType == FRESH) {
drawFresh(canvas);
}
// 每秒绘制一次
postInvalidateDelayed(1000 - milliSecond);
}
// 初始化attr
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyClockView);
mBackground = ta.getColor(R.styleable.MyClockView_wb_background_color, Color.WHITE);
mColorLong = ta.getColor(R.styleable.MyClockView_wb_scale_long_color, Color.BLACK);
mColorLargeLong = ta.getColor(R.styleable.MyClockView_wb_scale_large_long_color, mColorLong);
mColorShort = ta.getColor(R.styleable.MyClockView_wb_scale_short_color, Color.BLACK);
mHourPointColor = ta.getColor(R.styleable.MyClockView_wb_hour_pointer_color, Color.BLACK);
mMinutePointColor = ta.getColor(R.styleable.MyClockView_wb_minute_pointer_color, Color.BLACK);
mSecondPointColor = ta.getColor(R.styleable.MyClockView_wb_second_pointer_color, Color.BLACK);
mType = ta.getInt(R.styleable.MyClockView_wb_type, NORMAL);
mRadius = ta.getDimension(R.styleable.MyClockView_wb_radius, - 1); //如果未设置半径,默认值为-1
if (mType == NORMAL) {
background = BitmapFactory.decodeResource(getResources(), R.drawable.clock);
}
ta.recycle();
}
// 初始化画笔
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
// 计算宽度
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
//若长度为wrap_content,如果未设置半径,把长度设为200,否则长度为半径的两倍
if (specMode == MeasureSpec.AT_MOST) {
if (mRadius == - 1) {
result = 200;
} else {
result = (int) (mRadius * 2);
}
}
}
return result;
}
// 计算高度
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
return specSize;
} else {
if (specMode == MeasureSpec.AT_MOST) {
if (mRadius == - 1) {
result = 200;
} else {
result = (int) (mRadius * 2);
}
}
}
return result;
}
// 绘制普通风格
private void drawNormal(Canvas canvas) {
//绘制背景
canvas.drawBitmap(background, null, backgroundRect, null);
// 绘制刻度
canvas.save();
mPaint.setColor(mColorLargeLong);
mPaint.setStrokeWidth(oneUnit);
mPaint.setStyle(Paint.Style.FILL);
for (int i = 0; i <= 12; i++) {
canvas.drawLine(0, -oneUnit * 78, 0, -oneUnit * 73, mPaint);
canvas.rotate(30);
}
canvas.restore();
// 绘制时针
canvas.save();
float hourWidth = (float) (oneUnit * 1.6);
mPaint.setColor(mHourPointColor);
mPaint.setStrokeWidth(0);
Path path = new Path();
path.moveTo(-hourWidth, oneUnit * 15);
path.lineTo(hourWidth, oneUnit * 15);
path.lineTo(oneUnit, -oneUnit * 50);
path.lineTo(-oneUnit, -oneUnit * 50);
path.close();
canvas.rotate(angleHour);
canvas.drawPath(path, mPaint);
canvas.restore();
// 绘制分针
canvas.save();
mPaint.setColor(mMinutePointColor);
path.moveTo((float) (-oneUnit * 1.5), oneUnit * 15);
path.lineTo((float) (oneUnit * 1.5), oneUnit * 15);
path.lineTo((float) (oneUnit * 0.8), -oneUnit * 63);
path.lineTo((float) (-oneUnit * 0.8), -oneUnit * 63);
path.close();
canvas.rotate(angleMinute);
canvas.drawPath(path, mPaint);
canvas.restore();
// 绘制秒针
canvas.save();
mPaint.setColor(mSecondPointColor);
mPaint.setStrokeWidth((float) (oneUnit * 1.6));
canvas.rotate(angleSecond);
canvas.drawLine(0, -oneUnit * 72, 0, oneUnit * 15, mPaint);
canvas.restore();
// 绘制中心点
mPaint.setStrokeWidth(0);
canvas.drawCircle(0, 0, oneUnit * 4, mPaint);
}
// 绘制清新风格
private void drawFresh(Canvas canvas) {
canvas.save();
mPaint.setColor(mBackground);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(0, 0, mRadius, mPaint);
canvas.restore();
canvas.save();
mPaint.setColor(mColorLargeLong);
mPaint.setStrokeWidth(0);
RectF largeLongRectF = new RectF(-oneUnit, -mRadius, oneUnit, (-mRadius + oneUnit * 20));
for (int i = 0; i <= 4; i++) {
canvas.drawRoundRect(largeLongRectF, oneUnit, oneUnit, mPaint);
canvas.rotate(90);
}
mPaint.setColor(mColorLong);
RectF longRectF = new RectF(-oneUnit, -mRadius, oneUnit, (-mRadius + oneUnit * 31));
for (int i = 0; i <= 12; i++) {
if (i % 3 != 0) {
canvas.drawRoundRect(longRectF, oneUnit, oneUnit, mPaint);
}
canvas.rotate(30);
}
mPaint.setColor(mColorShort);
RectF shortRectF = new RectF(-oneUnit, -mRadius, oneUnit, -mRadius + oneUnit * 6);
for (int i = 0; i <= 60; i++) {
if (i % 5 != 0) {
canvas.drawRoundRect(shortRectF, oneUnit, oneUnit, mPaint);
}
canvas.rotate(6);
}
canvas.restore();
canvas.save();
float hourWidth = (float) (oneUnit * 3.5);
RectF hourRectF = new RectF(-hourWidth, -oneUnit * 61, hourWidth, 0);
mPaint.setColor(mHourPointColor);
canvas.rotate(angleHour);
canvas.drawRoundRect(hourRectF, hourWidth, hourWidth, mPaint);
canvas.restore();
canvas.save();
RectF minRectF = new RectF(-hourWidth, -oneUnit * 78, hourWidth, 0);
mPaint.setColor(mMinutePointColor);
canvas.rotate(angleMinute);
canvas.drawRoundRect(minRectF, hourWidth, hourWidth, mPaint);
canvas.restore();
canvas.save();
float secondWidth = (float) (oneUnit * 1.5);
RectF secondRectF = new RectF(-secondWidth, -mRadius, secondWidth, oneUnit * 20);
mPaint.setColor(mSecondPointColor);
canvas.rotate(angleSecond);
canvas.drawRoundRect(secondRectF, secondWidth, secondWidth, mPaint);
canvas.restore();
mPaint.setColor(mHourPointColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth((float) (oneUnit * 2.5));
canvas.drawCircle(0, 0, (float) (oneUnit * 5.25), mPaint);
mPaint.setColor(mSecondPointColor);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(0);
canvas.drawCircle(0, 0, oneUnit * 4, mPaint);
}
}
添加到 Activity:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.ivy.module.app.MainActivity">
<com.ivy.module.app.MyClockView
android:layout_width="220dp"
android:layout_height="220dp"
android:layout_gravity="center_horizontal"
custom:wb_hour_pointer_color="@android:color/black"
custom:wb_minute_pointer_color="#A996FF"
custom:wb_second_pointer_color="#FF0000"
custom:wb_scale_large_long_color="#cec8d8"
custom:wb_scale_long_color="#cec8d8"
custom:wb_type="1"/>
<com.ivy.module.app.MyClockView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
custom:wb_radius="120dp"
custom:wb_hour_pointer_color="#413c5a"
custom:wb_minute_pointer_color="#BCACEE"
custom:wb_second_pointer_color="#FF3B7A"
custom:wb_scale_large_long_color="#9888E0"
custom:wb_scale_long_color="#D4CBF2"
custom:wb_scale_short_color="#CBC5E9"
custom:wb_type="2"/>
</LinearLayout>
效果图: