首先看下效果
简单描述下这篇文章需要用到的技术:
View的测量
Canvas绘图技巧
触摸事件的计算
接口回调
整体思路
需要画两个圆弧一个是底部固定不变的另一个是更随手指移动渐变的圆弧
圆环上的分割线可以利用画布的旋转进行画线
文字的显示需要根据到圆心的距离进行计算。
根据手指滑动的x、y求当前滑动的范围的角度根据起始度数相加。
代码解读
画底部圆环
使用drawArc(RectF oval,float startAngle, float sweepAngle, boolean useCenter, Paint paint)
参数解读:
oval:圆环外的矩形
startAngle:开始角度
sweepAngle:扫描角度
useCenter:是否和圆心连线
paint:画笔
这里简单说下开始的0度角
mArcRectf = new RectF(mCenter - mRadius, mCenter - mRadius, mCenter + mRadius, mCenter + mRadius); //画底部纯白色圆 canvas.drawArc(mArcRectf, 135, 270, false, mArcPaint);
2.画带渐变色的圆,渐变色使用的是SweepGradient
int[] colors = {0xFFE5BD7D, 0xFFFAAA64, 0xFFFFFFFF, 0xFF6AE2FD, 0xFF8CD0E5, 0xFFA3CBCB, 0xFFBDC7B3, 0xFFD1C299, 0xFFE5BD7D,}; //渐变色 mSweepGradient = new SweepGradient(mCenter, mCenter, colors, null); // 设置画笔渐变色 mArcPaint.setShader(mSweepGradient); // 画带渐变色的圆 canvas.drawArc(mArcRectf, 135, scanDegrees, false, mArcPaint);
3.画线每隔3度旋转故整个圆需要120条线
//画线 每隔3度画一天线 整个圆共画120条线 for (int i = 0; i < 120; i++) { //圆心正顶部直线的Y坐标 top = mCenter - mRadius - circleWidth / 2; // 去除底部不包含的区域 只旋转不划线 if (i <= 45 || i >= 75) { if (i % 15 == 0) { //整点时Y轴向外延伸 top = top - 25; } canvas.drawLine(mCenter, mCenter - mRadius + 30, mCenter, top, mLinePaint); } //旋转 canvas.rotate(3, mCenter, mCenter); }
4.画文字这个比较复杂些需要计算x、y轴坐标
由图可以看出构成一个夹角为45度的直角三角形,斜边z的长度为mRadius + circleWidth / 2 + 45;
circleWidth :为圆的宽度
45为文字离圆的距离
所以x点为:
//斜边 int c = mRadius + circleWidth / 2 + 45; Log.e("CXX", "x:" + x); x = (int) Math.sqrt((c * c / 2));
下面是画文字的代码
//画文字+45代表这个字距离圆外边的距离 //x代表文字的中心距离圆心的距离 这是原点中心正左边字的长度 int x = mRadius + circleWidth / 2 + 45; //斜边 int c = mRadius + circleWidth / 2 + 45; x = (int) Math.sqrt((c * c / 2)); canvas.drawText(startDegrees + "", mCenter - x, mCenter + x, mTextPaint); canvas.drawText((startDegrees + 2) + "", mCenter - c, mCenter + 10, mTextPaint); canvas.drawText((startDegrees + 4) + "", mCenter - x, mCenter - x + 10, mTextPaint); canvas.drawText((startDegrees + 6) + "", mCenter, mCenter - c + 10, mTextPaint); canvas.drawText((startDegrees + 8) + "", mCenter + x, mCenter - x + 10, mTextPaint); canvas.drawText((startDegrees + 10) + "", mCenter + c, mCenter + 10, mTextPaint); canvas.drawText((startDegrees + 12) + "", mCenter + x, mCenter + x, mTextPaint);
这样整个界面就完成了但是还没加上滑动监听。
手指滑动需要得到当前手指所在象限才好计算度数因为每个象限和圆心的夹角都是0~90度。
首先先计算每个象限的夹角
可以根据手指移动的x点计算出cos然后求出对应的角度。
float v = x / (float) Math.sqrt(x * x + y * y); // 根据cos求角度 double acos = Math.acos(v); acos = Math.toDegrees(acos);
整个计算角度的代码如下:
float y = event.getY();
float x = event.getX();
float firstX = event.getX();
float firstY = event.getY();
//判断当前手指距离圆心的距离 代表在圆心的右侧
if (x > mCenter) {
x = x - mCenter;
} else {
x = mCenter - x;
}
if (y < mCenter) {
y = mCenter - y;
} else {
y = y - mCenter;
}
Log.e(“CXX”, “sqrt” + Math.sqrt(x * x + y * y) + “radius” + (mRadius - 40));
//判断当前手指是否在空白区域
if (Math.sqrt(x * x + y * y) < (mRadius - 40)) {
Log.e(“CXX”, “终止滑动”);
isCanMove = false;
return false;
}
float v = x / (float) Math.sqrt(x * x + y * y);
// 根据cos求角度
double acos = Math.acos(v);
acos = Math.toDegrees(acos);
Log.e(“CXX”, “acos” + acos);
//手指在第三象限
if (firstX <= mCenter && firstY >= mCenter) {
Log.e(“cxx”, “第三象限”);
acos = 180 - acos;
} else if (firstX <= mCenter && firstY <= mCenter) {
//手指在第二象限
acos = acos + 180;
Log.e(“CXX”, “第二象限”);
} else if (firstX >= mCenter && firstY <= mCenter) {
//手指在第一象限
acos = 360 - acos;
Log.e(“CXX”, “第一象限”);
} else {
Log.e(“CXX”, “第四象限”);
}
Log.e(“cxx”, “v” + acos);
scanDegrees = (int) acos;
//计算度数 每22.5度值+1
if (scanDegrees >= 135 && scanDegrees <= 360) {
scanDegrees = scanDegrees - 135;
int degrees = (int) (scanDegrees / 22.5);
Log.e(“CXX”, “当前度数” + (degrees + startDegrees));
finalDegrees = degrees;
if (moveInterface != null) {
moveInterface.getCurrentDegrees((degrees + startDegrees));
}
invalidate();
} else if (scanDegrees <= 65) { //小于45无法算到最后一个 故意多加了20度 scanDegrees = (int) (360 - 135 + acos); if (scanDegrees > 270) { scanDegrees = 270; } int degrees = (int) (scanDegrees / 22.5); Log.e("CXX", "当前度数" + (degrees + startDegrees)); finalDegrees = degrees; if (moveInterface != null) { moveInterface.getCurrentDegrees((degrees + startDegrees)); } if (scanDegrees > 270) { return false; } invalidate(); Log.e("CXX", "onTouchEvent scanDegrees" + scanDegrees); } else { scanDegrees = 270; } return true;
这样整个程序就完成了。