這裏是源碼地址: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);
}