这里是源码地址:http://download.csdn.net/detail/h55l55/9521851
首先简单介绍一下这个选择器,可以旋转表针的方式旋转选择时间,然后在下一个视图中选择分钟数。通过两个接口回调来返回选择的结果。接下来说一下编程思路:
1、确定需要的颜色常量,以及可以改变值得常量
/**
* 默认外圆半径
*/
private final static int DEFAULT_OUT_RADIO = 100;
/**
* 默认选择小圆的半径
*/
private final static int DEFAULT_CHECK_RAIDO_WIDTH = 20;
/**
* 外部文字未选中颜色
*/
private final static int DEFAULT_OUT_TEXT_UNCHECKED_COLOR = 0xff1f1f1f;
/**
* 内部文字未选中颜色
*/
private final static int DEFAULT_IN_TEXT_UNCHECKED_COLOR = 0xff6d6d6d;
/**
* 外部文字选中颜色
*/
private final static int DEFAULT_OUT_TEXT_CHECKED_COLOR = 0xffffffff;
/**
* 内部文字选中颜色
*/
private final static int DEFAULT_IN_TEXT_CHECKED_COLOR = 0xffffffff;
/**
* 连接线条颜色
*/
private final static int DEFAULT_LINE_COLOR = 0xffff6633;
/**
* 外部文字大小
*/
private final static int DEFAULT_OUT_TEXT_SIZE = 14;
/**
* 内部文字大小
*/
private final static int DEFAULT_IN_TEXT_SIZE = 10;
/**
* 时间改变监听器
*/
private onHourChangedListener listener;
/** 选中时候圆圈的半径 */
private int checkCircleRadio;
/** 未选中外部文字颜色 */
private int mOutUnCheckedColor;
/** 未选中内部文字颜色 */
private int mInUncheckedColor;
/** 外部选中文字颜色 */
private int mOutTextCheckedColor;
/** 外部选中文字颜色 */
private int mInTextCheckedColor;
/** 线的颜色 */
private int mLineColor;
/** 外部文字大小 */
private int mOutTextSize;
/** 内部文字大小 */
private int mInTextSize;
/** 画表盘数字的画笔 */
private Paint mTextPaint = new Paint();;
/** 外面圆的半径 */
private int mOutRadius;
/** 内部圆的半径 */
private int mInRadius;
/** 单前选中的小时数 */
private Integer hours;
/** 当前视图的宽度 */
private int mViewWight;
2、初始化画笔的值
在此之前需要重写构造方法,然后在构造方法中调动此方法。
/** 给变量赋值,可以使用自定义属性 */
private void init() {
mOutUnCheckedColor = DEFAULT_OUT_TEXT_UNCHECKED_COLOR;
mInUncheckedColor = DEFAULT_IN_TEXT_UNCHECKED_COLOR;
mOutTextCheckedColor = DEFAULT_OUT_TEXT_CHECKED_COLOR;
mLineColor = DEFAULT_LINE_COLOR;
mOutTextSize = sp2px(getContext(), DEFAULT_OUT_TEXT_SIZE);
mInTextCheckedColor = DEFAULT_IN_TEXT_CHECKED_COLOR;
mInTextSize = sp2px(getContext(), DEFAULT_IN_TEXT_SIZE);
mOutRadius = dp2px(getContext(), DEFAULT_OUT_RADIO);
checkCircleRadio = dp2px(getContext(), DEFAULT_CHECK_RAIDO_WIDTH);
mInRadius = mOutRadius - 3 * mOutTextSize;
}
3、在Ondraw中开始绘制选择圈
在onDraw中调用画圈和画数字的方法
@Override
protected void onDraw(Canvas canvas) {
canvas.translate(mViewWight / 2, mViewWight / 2);
drawCircle(canvas);
drawNumber(canvas);
}
画圈的方法,用来绘制表的指针和选中后的圆圈
/** 画选数字的圆圈 */
private void drawCircle(Canvas canvas) {
mTextPaint.setTextSize(mOutTextSize);
if (hours > 12 || hours == 0) {// 13点到00点
mTextPaint.setColor(mLineColor);
mTextPaint.setStrokeWidth(checkCircleRadio / 20);
Point point = getPoint(hours * 30, mInRadius);
int y = point.y;
canvas.drawCircle(point.x, y, checkCircleRadio, mTextPaint);
canvas.drawLine(0, 0, point.x, y, mTextPaint);
} else {// 1到12点
mTextPaint.setColor(mLineColor);
Point point = getPoint(hours * 30, mOutRadius);
int y = point.y;
canvas.drawCircle(point.x, y, checkCircleRadio, mTextPaint);
canvas.drawLine(0, 0, point.x, y, mTextPaint);
}
canvas.drawCircle(0, 0, checkCircleRadio / 10, mTextPaint);
}
4、在ondraw中开始绘制数字表盘
画表盘的方法,用来绘制选择时候的数字
/** 画数字表盘 */
private void drawNumber(Canvas canvas) {
mTextPaint.setAntiAlias(true);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setTextSize(mOutTextSize);
// 所绘制文字宽高
int textWight;
for (int i = 1; i < 13; i++) {
if (hours == i) {
mTextPaint.setColor(mOutTextCheckedColor);
} else {
mTextPaint.setColor(mOutUnCheckedColor);
}
textWight = (int) mTextPaint.measureText(i + "");
Point point = getPoint(i * 30, mOutRadius);
canvas.drawText(i + "", point.x - textWight / 2,
point.y + mOutTextSize / 2, mTextPaint);
}
mTextPaint.setTextSize(mInTextSize);
for (int i = 13; i < 25; i++) {
if (hours == i) {
mTextPaint.setColor(mInTextCheckedColor);
} else {
mTextPaint.setColor(mInUncheckedColor);
}
textWight = (int) mTextPaint.measureText("00");
String text = i + "";
if (i == 24) {
text = "00";
if (hours == 0) {
mTextPaint.setColor(mInTextCheckedColor);
}
}
Point point = getPoint((i - 12) * 30, mInRadius);
canvas.drawText(text, point.x - textWight / 2,
point.y + mInTextSize / 2, mTextPaint);
}
}
getPoint方法,用来获取当前旋转角度下的坐标
/** 获取在角度为degree,半径为radio时候点的坐标 */
private Point getPoint(int degree, int radio) {
int y = (int) (-Math.cos(2 * Math.PI / 360 * degree) * radio);
int x = (int) (Math.sin(2 * Math.PI / 360 * degree) * radio);
return new Point(x, y);
}
5、重写onTouchEvent方法,获取手指的坐标,然后重新绘制,使圆圈移动到指定位置
重写onTouchEvent方法
/** 监听触摸事件 */
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
hours = getTouchHours(event.getX(), event.getY());
invalidate();
break;
case MotionEvent.ACTION_MOVE:
// 获取属于哪个时间点
// 更改时间属性
hours = getTouchHours(event.getX(), event.getY());
// 重绘
invalidate();
break;
case MotionEvent.ACTION_UP:
if (hours < 13) {
Toast.makeText(getContext(), hours + "", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getContext(), hours + "", Toast.LENGTH_SHORT).show();
}
if (null != listener) {
listener.onHourChanged(hours, true);
}
break;
}
return true;
}
获取当前点击位置对应的时间
/** 获取当前触摸点对应的小时值 */
private int getTouchHours(float x, float y) {
x = x - mViewWight / 2;
y = y - mViewWight / 2;
for (int i = 0; i < 360; i += 30) {
if (isInOutCircle(i, x, y)) {
if (i == 0) {
return 12;
}
return i / 30;
}
if (isInInnerCircle(i, x, y)) {
return 12 + i / 30 == 12 ? 0 : 12 + i / 30;
}
}
return hours;
}
判断当前点击位置是否位于指定的时间处
/** 坐标是否落在外部圆上 */
private boolean isInOutCircle(int angle, float x, float y) {
// DateUtils.getDayOfWeekString(Calendar.MONDAY, abbrev)
// 获取x坐标的范围
int xx = (int) ((Math.sin(2 * Math.PI / 360 * angle)) * mOutRadius);
int yy = -(int) ((Math.cos(2 * Math.PI / 360 * angle)) * mOutRadius);
// (0,-1440)
// xx-textHeight<x<xx+textHeight
if (x < xx - checkCircleRadio || x > xx + checkCircleRadio) {
return false;
}
if (yy > 0) {
if (y < yy - checkCircleRadio || y > yy) {
return false;
}
} else {
if (y < yy || y > yy + checkCircleRadio) {
return false;
}
}
return true;
}
/** 坐标是否落在内部圆上 */
private boolean isInInnerCircle(int angle, float x, float y) {
// 获取x坐标的范围
int xx = (int) ((Math.sin(2 * Math.PI / 360 * angle)) * mInRadius);
int yy = -(int) ((Math.cos(2 * Math.PI / 360 * angle)) * mInRadius);
// xx-textHeight<x<xx+textHeight
if (x < xx - checkCircleRadio || x > xx + checkCircleRadio) {
return false;
}
if (yy > 0) {
if (y < yy - checkCircleRadio || y > yy) {
return false;
}
} else {
if (y < yy || y > yy + checkCircleRadio) {
return false;
}
}
return true;
}
6、在手指抬起时候回调接口方法,把选择的时间传递出去
/** 设置时间回调监听器 */
public void setOnHourChangedListener(onHourChangedListener listener) {
this.listener = listener;
}
/** 当时间改变时候回调 */
interface onHourChangedListener {
void onHourChanged(int hour, boolean isUp);
}