1. 绘制表盘
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 初次进入该方法时,进行坐标数据的计算
if (!positionDataInitFlag) {
scale =310f /mClockBitmap.getWidth();
// 宽度中心取屏幕画布的一半处
standardX = (int) (LeXingApplation.screenWidthPixels /2f); // 240
// 高度取屏幕高度的0.43处
standardY = (int) (LeXingApplation.screenHeightPixels *0.2f);
clockWidth = (int) (LeXingApplation.screenWidthPixels *0.56f);
Log.e("tag", "standardX=" +standardX +"standardY=" +standardY);
clockHeight =clockWidth;
clockLeftX = (int) (standardX -clockWidth /2f);// 92
clockTopY = (int) (standardY -clockHeight /2f);// 196
hitAreaXLeft = (int) (-clockWidth /2f); // -148
hitAreaXRight = (int) (clockWidth /2f); // 148
hitAreaYTop = (int) (clockHeight /2f); //
hitAreaYBottom = (int) (-clockHeight /2f);
clockCenterSizeHalf = (int) (30f *clockWidth / (2f *310f));
// 指针的高度
mHourNormalHeight = (int) (clockWidth *0.5f *0.75f);
mHourPressHeight = (int) (clockWidth *0.5f *0.95f);
mMinuteNormalHeight = (int) (clockWidth *0.5f *0.95f);
mMinutePressHeight = (int) (clockWidth *0.5f *1.18f);
//
// 画表盘
clockSrc.left =0;
clockSrc.right =mClockBitmap.getWidth();
clockSrc.top =0;
clockSrc.bottom =mClockBitmap.getHeight();
clockDst.left =clockLeftX;
clockDst.top =clockTopY;
clockDst.right =clockLeftX +clockWidth;
clockDst.bottom =clockTopY +clockHeight;
// 表心
clockCenterSrc.left =0;
clockCenterSrc.right = TimeSetActivity.clockCenterBitmap.getWidth();
clockCenterSrc.top =0;
clockCenterSrc.bottom = TimeSetActivity.clockCenterBitmap
.getHeight();
clockCenterDst.right =standardX +clockCenterSizeHalf;
clockCenterDst.left =standardX -clockCenterSizeHalf;
clockCenterDst.bottom =standardY +clockCenterSizeHalf;
clockCenterDst.top =standardY -clockCenterSizeHalf;
// 帮助按钮
helpSrc.left =0;
helpSrc.right = TimeSetActivity.helpNormalBitmap.getWidth();
helpSrc.top =0;
helpSrc.bottom = TimeSetActivity.helpNormalBitmap.getHeight();
int helpButtonWidth = (int) (64f /30f *2f *clockCenterSizeHalf);
helpDst.left =clockDst.right;
helpDst.right =clockDst.right +helpButtonWidth;
helpDst.top =clockDst.top;
helpDst.bottom =clockDst.top +helpButtonWidth;
// 设置初始化位置数据标志位, 重新绘图时无需再次初始化
positionDataInitFlag =true;
}
// 画表盘
// 画出指定的位图,位图将自动-->缩放/自动转换,以填补目标矩形
if (mClockBitmap !=null) {
canvas.drawBitmap(mClockBitmap, clockSrc, clockDst, null);
}else {
mClockBitmap = TimeSetActivity.clockNormalBitmap;
canvas.drawBitmap(mClockBitmap, clockSrc, clockDst, null);
}
// 画表心
canvas.drawBitmap(TimeSetActivity.clockCenterBitmap, clockCenterSrc,
clockCenterDst, null);
if (mHelpBitmap ==null) {
mHelpBitmap = TimeSetActivity.helpNormalBitmap;
}
// 画帮助按钮
canvas.drawBitmap(mHelpBitmap, helpSrc, helpDst, null);
// Paint paint = new Paint();
// paint.setColor(Color.RED);
// // 画标准x y坐标辅助线
// canvas.drawLine(0, standardY, canvas.getWidth(), standardY, paint);
// canvas.drawLine(standardX, 0, standardX, canvas.getHeight(), paint);
}
2. 绘制时针
private void drawHour(Canvas canvas) {
canvas.save();
canvas.translate(standardX, standardY);
canvas.rotate(curTime.mHourDegree);
Paintpaint =new Paint();
paint.setAntiAlias(true);
int height =this.mHourPressFlag ?mHourPressHeight :mHourNormalHeight;
int width = (int) (height /10.5f);
paint.setColor(Color.DKGRAY);
hourRectF.left = -width /2;
hourRectF.right =width /2;
hourRectF.bottom =height *3.8f /5f;
hourRectF.top = -height *1.2f /5f;
canvas.drawRoundRect(hourRectF, 10, 10, paint);
canvas.restore();
}
3. 绘制分针
public void drawMinute(Canvas canvas) {
canvas.save();
canvas.translate(standardX, standardY);
canvas.rotate(curTime.mMinuteDegree);
Paintpaint =new Paint();
paint.setAntiAlias(true);
int height =this.mMinutePressFlag ?mMinutePressHeight
:mMinuteNormalHeight;
int width = (int) (height /15.5f);
paint.setColor(Color.DKGRAY);
minuteRectF.left = -width /2;
minuteRectF.right =width /2;
minuteRectF.bottom =height *3.8f /5f;
minuteRectF.top = -height *1.2f /5f;
canvas.drawRoundRect(minuteRectF, 10, 10, paint);
canvas.restore();
}
4. 命中区域判断
private boolean isHitArea(Point point) {
boolean flag = !(point.x hitAreaXRight
|| point.y >hitAreaYTop || point.y
if (!flag) {
// 如果没有命中, 则设置标志位
this.cursorHitStatus =0;
}
return flag;
}
5. 时分触控及是否被选中
private void doTouchDownWork(Point point) {
QuadranthandQuadant = ClockDegreeAdapter.getQuadrant(point);
// 当前分针的象限
QuadrantminuteQuadant = ClockDegreeAdapter
.getQuadrant(curTime.mMinuteDegree);
// 当前时针所在象限
QuadranthourQuadant = ClockDegreeAdapter
.getQuadrant(curTime.mHourDegree);
// 如果当前的分针或分针在event所在象限, 则进行处理,
// 如果时针和分针都在此象限, 则判断指针坐标与event靠近的那个进行重绘
// 否则不进行处理
cursorHitStatus =0;
// 时针分针不在同一个象限
if (minuteQuadant !=hourQuadant) {
if (handQuadant ==minuteQuadant) {
cursorHitStatus =1;
setClockStatus(true, false, true);
}else if (handQuadant ==hourQuadant) {
setClockStatus(true, true, false);
cursorHitStatus =2;
}
}
// 如果在时针分针在同一个象限
else if (minuteQuadant ==hourQuadant &&minuteQuadant ==handQuadant) {
int eventRadian = ClockDegreeAdapter.getAngle(point);
// 计算event与时针,分针的夹角的绝对值
int minuteCrossRadian = Math.abs(eventRadian
-curTime.mMinuteDegree);
int hourCrossRadian = Math.abs(eventRadian -curTime.mHourDegree);
// 比较夹角, 选择小的夹角的那个指针作为当前被重绘的对象
if (hourCrossRadian=2)
setClockStatus(true, true, false);
}else {
cursorHitStatus =1;
setClockStatus(true, false, true);
}
}
// 如果以上都没有命中, 则有可能指针在坐标轴上
if (cursorHitStatus ==0) {
// 可能是在坐标轴上
int eventRadian = ClockDegreeAdapter.getAngle(point);
if (curTime.mMinuteDegree %90 ==0) {
if (Math.abs((eventRadian >=345 ?360 -eventRadian
:eventRadian) -curTime.mMinuteDegree) <=15) {
// 分针与event指针在15度范围内被选中
cursorHitStatus =1;
setClockStatus(true, false, true);
}
}else if (curTime.mHourDegree %90 ==0) {
int hourDegree =curTime.mHourDegree >=360 ?curTime.mHourDegree -360
:curTime.mHourDegree;
if (Math.abs((eventRadian >=345 ?360 -eventRadian
:eventRadian) -hourDegree) <=15) {
// 时针与event指针在15度范围内被选中
cursorHitStatus =2;
setClockStatus(true, true, false);
}
}
}
}
6. Move时时分的计算
private void doTouchMoveWork(Point point) {
int pointDegree = ClockDegreeAdapter.getAngle(point);
if (cursorHitStatus ==1) {
curTime.mMinuteDegree =pointDegree;
}else if (cursorHitStatus ==2) {
// 如果是时针的场合, 以上一次的时针跟本次时针的差额进行时间的选取
int tempHourDegree =0;
int minus = Math.abs(preHourDegree -pointDegree);
if (minus <90) {
tempHourDegree =pointDegree;
}else if (minus >360
&& (pointDegree <90 && (preHourDegree <90 ||preHourDegree >600))) {
tempHourDegree =0;
}else {
tempHourDegree = (pointDegree +360) %980;
}
curTime.mHourDegree =tempHourDegree;
// 将上次数据保存
preHourDegree =curTime.mHourDegree;
// 24小时制的时间的角度为0~720度
curTime.mHour = ClockDegreeAdapter.getHour(curTime.mHourDegree);
}
}
7. onTouch的处理
@Override
public boolean onTouch(View v, MotionEvent event) {
Pointpoint =new Point();
int eventX = (int) event.getX();
int eventY = (int) event.getY();
point.x =eventX -standardX;
point.y =standardY -eventY;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (eventX >=helpDst.left &&eventX <=helpDst.right
&&eventY >=helpDst.top &&eventY <=helpDst.bottom) {
// 该状态位为true的场景下, toutch up的时候, 打开question detai帮助页面
helpHitFlag =true;
mHelpBitmap = TimeSetActivity.helpPressBitmap;
postInvalidate();
}
if (isHitArea(point)) {
// 只有在被命中区域才执行事件
doTouchDownWork(point);
calcDegree(point, false);
postInvalidate();
}
}
break;
case MotionEvent.ACTION_MOVE:
doTouchMoveWork(point);
// mClockBitmap = TimeSetActivity.clockPressBitmap;
calcDegree(point, false);
postInvalidate();
break;
case MotionEvent.ACTION_UP:
// 松开手,无条件切换成帮助按钮的没有选中的状态
mHelpBitmap = TimeSetActivity.helpNormalBitmap;
mClockBitmap = TimeSetActivity.clockNormalBitmap;
this.mHourPressFlag =false;
this.mMinutePressFlag =false;
calcDegree(point, true);
postInvalidate();
break;
}
return true;
}
8. 获取时分所在的弧度
private static double getRadianByPosEx(Point point) {
if (point.x ==0 && point.y ==0) {
return 0;
}
double Sin = Math.abs(point.x)
/ Math.sqrt(point.x * point.x + point.y * point.y);
double dAngle = Math.asin(Sin);
switch (getQuadrant(point)) {
case EQ_NONE: {
if (point.x ==0 && point.y ==0) {
return 0;
}
if (point.x ==0) {
if (point.y >0) {
return PI;
}else {
return 0;
}
}
if (point.y ==0) {
if (point.x >0) {
return (float) (1.5 *PI);
}else {
return PI /2;
}
}
}
break;
case EQ_ONE: {
return PI +dAngle;
}
case EQ_TWO: {
return PI -dAngle;
}
case EQ_THREE: {
return dAngle;
}
case EQ_FOUR: {
return 2 *PI -dAngle;
}
}
return 0;
}
9. 获取点所在的角度及对应的分钟数,小时数
public static int getAngle(Point point) {
double dAngle =getRadianByPosEx(point);
return (int) (dAngle * (360 / (2 *PI)));
}
public static int getMinute(int angle) {
return (angle /6 +30) %60;
}
public static int getHour(int angle) {
if (angle <540) {
return 6 + angle /30;
}else {
return (angle -540) /30;
}
}
10. 返回时针校验角度
public static int getUnionHourAngle(int hour24, int minute) {
int hourDegreePlus = minute /2;
int hour = hour24 >12 ? hour24 -12 : hour24;
hour =hour <6 ?12 +hour :hour;
// if (hour24 >= 18 || hour24 <= 5) {
// return (hour - 6) * 30 + +360 + hourDegreePlus;
// }
return (hour -6) *30 +hourDegreePlus;
}