其中舵轮A是一个圆形图片,指针是另一个图片
package com.kongqw.rockerlibrary.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import androidx.core.content.res.ResourcesCompat;
import com.test.demo.R;
public class Clockwidget extends View {
private final String TAG = "ClickWidget";
private boolean isChecked = false;
private float mCenterPointX;
private float mCenterPointY;
private Paint paint;
private int height;
private int width;
private int radius;
static final float[] COMMON = {-90, 180};
private float horizontalX = 0;
private float horizontalY = 0;
private final float pointerStartDegrees = COMMON[0];
private double currentRealAngle = COMMON[1];
private float pointerShouldRotationDegrees = pointerStartDegrees;
private Drawable mClockNormal;
private Drawable mClockPressed;
private int centerWheelWidth;
private int centerWheelHeight;
//private float rotationDegrees = -180f;
// private float rotationDegrees = 90f;
// private float rotationDegrees = 0f;
public Clockwidget(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private Bitmap aWheelBitmap, aWheelBitmapPressed;
private Bitmap acurrentWheelBitmap;
private Bitmap pointerBitmap;
private void init() {
// Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_normal, null);
// aWheelBitmap = drawableToBitmap(drawable, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
// Drawable drawableApressed = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_pressed, null);
// aWheelBitmapPressed = drawableToBitmap(drawableApressed, drawableApressed.getIntrinsicWidth(), drawableApressed.getIntrinsicHeight());
aWheelBitmap = getChildNormalBitmap();
aWheelBitmapPressed = getChildPressedBitmap();
acurrentWheelBitmap = aWheelBitmap;
centerWheelWidth = acurrentWheelBitmap.getWidth();
centerWheelHeight = acurrentWheelBitmap.getHeight();
Drawable drawablePointer = ResourcesCompat.getDrawable(getResources(), R.mipmap.ic_wheel_pointer, null);
pointerBitmap = drawableToBitmap(drawablePointer, (int) (drawablePointer.getIntrinsicHeight() * 0.6), (int) (drawablePointer.getIntrinsicHeight() * 0.6));
paint = new Paint();
paint.setColor(getResources().getColor(R.color.black));
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
paint.setTextSize(100);
}
/**
* 安卓drawable转bitmap 并控制大小
*
* @param drawable
* @param width
* @param height
* @return
*/
public static Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
height = getMeasuredHeight();
width = getMeasuredWidth();
radius = (int) (Math.min(height, width) * 0.2);
mainBgRadius = (int) (radius * 1.2);
mCenterPointX = ((float) width / 2);
mCenterPointY = ((float) height / 2);
horizontalY = mCenterPointY;
mCenterPoint = new Point((int) mCenterPointX, (int) mCenterPointY);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawMainBg(canvas);
drawWheel(canvas);
drawLineAndText(canvas);
drawPointer(canvas);
}
private void drawWheel(Canvas canvas) {
if (acurrentWheelBitmap != null) {
int bitmapWidth = acurrentWheelBitmap.getWidth();
int bitmapHeight = acurrentWheelBitmap.getHeight();
int viewWidth = getWidth();
int viewHeight = getHeight();
int x = (viewWidth - bitmapWidth) / 2;
int y = (viewHeight - bitmapHeight) / 2;
canvas.save(); // 保存绘图状态
canvas.rotate(0, x + bitmapWidth / 2, y + bitmapHeight / 2); // 旋转绘图坐标系
canvas.drawBitmap(acurrentWheelBitmap, x, y, null); // 绘制Bitmap
canvas.restore(); // 恢复绘图状态
}
}
/**
* 绘制指针
*
* @param canvas
*/
private void drawPointer(Canvas canvas) {
int bitmapWidth = pointerBitmap.getWidth();
int bitmapHeight = pointerBitmap.getHeight();
int x = (int) (mCenterPointX - (bitmapWidth / 2));
int y = (int) (mCenterPointY - radius);
canvas.save(); // 保存绘图状态
canvas.rotate(pointerShouldRotationDegrees, mCenterPointX, mCenterPointY); // 旋转绘图坐标系
canvas.drawBitmap(pointerBitmap, x, y, null); // 绘制Bitmap
canvas.restore(); // 恢复绘图状态
}
/**
* 绘制刻度和文字
*
* @param canvas
*/
private void drawLineAndText(Canvas canvas) {
drawScale2(canvas);
drawScaleText(canvas);
}
float hourScaleHeight = 30;
float minuteScaleHeight = 20;
private void drawScale2(Canvas canvas) {
paint.reset();
paint.setAntiAlias(true);
paint.setColor(getResources().getColor(R.color.kedu_color));
paint.setStrokeWidth(2);
paint.setTextSize(20);
float cy = mCenterPointY;
float cx = mCenterPointX;
float radius = mainBgRadius;
for (int i = 0; i < 60; i++) {
double a = i * 6 * Math.PI / 180;
float startX = (float) (cx + Math.sin(a) * radius);
float startY = (float) (cy - Math.cos(a) * radius);
if (i % 5 == 0) {
float endX = (float) (cx + Math.sin(a) * (radius - hourScaleHeight));
float endY = (float) (cy - Math.cos(a) * (radius - hourScaleHeight));
canvas.drawLine(startX, startY, endX, endY, paint);
} else {
float endX = (float) (cx + Math.sin(a) * (radius - minuteScaleHeight));
float endY = (float) (cy - Math.cos(a) * (radius - minuteScaleHeight));
canvas.drawLine(startX, startY, endX, endY, paint);
}
}
}
String[] hourTexts = {"90", "60", "30", "0", "-30", "-60", "-90", "-60", "-30", "0", "30", "60"};
private void drawScaleText(Canvas canvas) {
float cy = mCenterPointY;
float cx = mCenterPointX;
float radius = mainBgRadius;
paint.reset();
paint.setTextSize(20);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
paint.setColor(getResources().getColor(R.color.color_wheel_text));
//文字是从宽度的中间绘制的
paint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float ascent = fontMetrics.ascent;
float descent = fontMetrics.descent;
for (int i = 0; i < 12; i++) {
float textWidth = paint.measureText(hourTexts[i]);
double a = i * 30 * Math.PI / 180;
//hourScaleHeight刻度的长度
//cx + sina * (radius - hourScaleHeight) 就是刻度点的x坐标,也就是绘制文字的中心点,再减少文字宽度的一半就
//可以不遮挡了
//原来的 float baseX = (float) (cx + Math.sin(a) * (radius - hourScaleHeight - textWidth / 2));
float baseX = (float) (cx + Math.sin(a) * (radius + hourScaleHeight - textWidth / 2));
//cy - cosa * (radius - hourScaleHeight) 就是刻度点的y坐标,cos在圆的上半部分是正值,下半部分是负值
//在左右圆是0,光减刻度是不行的,还要减ascent,在12点方向需要减,6点方向
// 原来的 float baseY = (float) (cy - Math.cos(a) * (radius - hourScaleHeight - Math.abs(ascent * Math.cos(a))));
float baseY = (float) (cy - Math.cos(a) * (radius + hourScaleHeight - Math.abs(ascent * Math.cos(a))));
//baseY == 刻度的y显示会不对齐,所以用中心
//baseY 向上移动到文字的中心,middle = (|ascent| + descent)/2
//baseY 到 middle的距离就是 middle - descent
baseY += ((Math.abs(ascent) - descent) / 2f);
canvas.drawText(hourTexts[i], baseX, baseY, paint);
}
}
private int mainBgRadius = 0;
private void drawMainBg(Canvas canvas) {
paint.reset();
paint.setAntiAlias(true);
paint.setColor(getResources().getColor(R.color.color_wheel_bg));
canvas.drawCircle(mCenterPointX, mCenterPointY, mainBgRadius, paint);
}
private Point mCenterPoint;
@Override
public boolean onTouchEvent(MotionEvent event) {
float cWheelStartX = (mCenterPointX - (centerWheelWidth / 2));
float cWheelStartY = (mCenterPointY - (centerWheelHeight / 2));
float cWheelEndX = (mCenterPointX + (centerWheelWidth / 2));
float cWheelEndY = (mCenterPointY + (centerWheelHeight / 2));
float moveX = event.getX();
float moveY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:// 按下
// 回调 开始
if (moveX > cWheelStartX && moveX < cWheelEndX &&
moveY > cWheelStartY && moveY < cWheelEndY) {
isChecked = !isChecked;
if (isChecked) {
setClockPressed();
} else {
setClockNormal();
}
}
if (mAngleChangedListener != null) {
mAngleChangedListener.onAngleCheck(isChecked);
}
break;
case MotionEvent.ACTION_MOVE:// 移动
if (moveX < cWheelStartX || moveX > cWheelEndX || moveY > cWheelEndY || moveY < cWheelStartY) {
if (isChecked) {
getRockerPositionPoint(mCenterPoint, new Point((int) moveX, (int) moveY), pointerBitmap.getWidth(), mainBgRadius);
}
}
break;
case MotionEvent.ACTION_UP:// 抬起
if (moveX < cWheelStartX || moveX > cWheelEndX || moveY > cWheelEndY || moveY < cWheelStartY) {
if (mAngleChangedListener != null) {
mAngleChangedListener.onAngleTouchUp(this.angleForUser, isChecked);
}
}
break;
case MotionEvent.ACTION_CANCEL:// 移出区域
break;
}
return true;
}
/**
* 获取摇杆实际要显示的位置(点)
*
* @param centerPoint 中心点
* @param touchPoint 触摸点
* @param regionRadius 摇杆可活动区域半径
* @param rockerRadius 摇杆半径
* @return 摇杆实际显示的位置(点)
*/
private Point getRockerPositionPoint(Point centerPoint, Point touchPoint, float regionRadius, float rockerRadius) {
// 两点在X轴的距离
float lenX = (float) (touchPoint.x - centerPoint.x);
// 两点在Y轴距离
float lenY = (float) (touchPoint.y - centerPoint.y);
// 两点距离
float lenXY = (float) Math.sqrt((double) (lenX * lenX + lenY * lenY));
// 计算弧度
double radian = Math.acos(lenX / lenXY) * (touchPoint.y < centerPoint.y ? -1 : 1);
// 计算角度
double angle = radian2Angle(radian);
currentRealAngle = angle;
Log.e(TAG, "currentRealAngle = " + currentRealAngle);
setAngel(angle);
// 回调 返回参数
// callBack(angle);
// Log.i(TAG, "getRockerPositionPoint: 角度 :" + angle + " 应该给出的angle = " + (180 - (360 - angle)));
// if (angle >= 180 && angle < 270) {
//
// double degreeCallBack = (180 - (360 - angle));
// pointerShouldRotationDegrees = (float) (pointerStartDegrees + degreeCallBack);
//
// this.angleForUser = degreeCallBack;
// // Log.i(TAG, "getRockerPositionPoint: 角度 :" + angle + " 应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
// invalidate();
// } else if (angle >= 270 && angle < 360) {
// //应该给出的是0到90度
// double degreeCalucate = 90 - (360 - angle);
// pointerShouldRotationDegrees = (float) (degreeCalucate);
// double degreeCallBack = 90 - degreeCalucate;
//
// this.angleForUser = degreeCallBack;
// // Log.i(TAG, "getRockerPositionPoint: 角度 :" + angle + " 应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
// invalidate();
// } else if (angle > 0 && angle <= 90) {
// pointerShouldRotationDegrees = (float) (90 + angle);
// double degreeCallBack = -angle;
// this.angleForUser = degreeCallBack;
// // Log.i(TAG, "getRockerPositionPoint: 角度 :" + angle + " 应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
// invalidate();
// } else {
// pointerShouldRotationDegrees = (float) (angle - 90 + 180);
// double degreeCallBack = -(90 - (pointerShouldRotationDegrees - 180));
// this.angleForUser = degreeCallBack;
// // Log.i(TAG, "getRockerPositionPoint: 角度 :" + angle + " 应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
// invalidate();
// }
// if (this.angleForUser == -180) {
// this.angleForUser = 0;
// }
// if (mAngleChangedListener != null) {
// mAngleChangedListener.onAngleChanged(this.angleForUser);
// }
if (lenXY + rockerRadius <= regionRadius) { // 触摸位置在可活动范围内
return touchPoint;
} else { // 触摸位置在可活动范围以外
// 计算要显示的位置
int showPointX = (int) (centerPoint.x + (regionRadius - rockerRadius) * Math.cos(radian));
int showPointY = (int) (centerPoint.y + (regionRadius - rockerRadius) * Math.sin(radian));
return new Point(showPointX, showPointY);
}
}
private void setAngel(double angle) {
if (angle >= 180 && angle < 270) {
double degreeCallBack = (180 - (360 - angle));
pointerShouldRotationDegrees = (float) (pointerStartDegrees + degreeCallBack);
this.angleForUser = degreeCallBack;
Log.i(TAG, "1 getRockerPositionPoint: 角度 :" + angle + " 应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
invalidate();
} else if (angle >= 270 && angle < 360) {
//应该给出的是0到90度
double degreeCalucate = 90 - (360 - angle);
pointerShouldRotationDegrees = (float) (degreeCalucate);
double degreeCallBack = 90 - degreeCalucate;
this.angleForUser = degreeCallBack;
Log.i(TAG, "2 getRockerPositionPoint: 角度 :" + angle + " 应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
invalidate();
} else if (angle > 0 && angle <= 90) {
pointerShouldRotationDegrees = (float) (90 + angle);
double degreeCallBack = -angle;
this.angleForUser = degreeCallBack;
Log.i(TAG, "3 getRockerPositionPoint: 角度 :" + angle + " 应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
invalidate();
} else {
pointerShouldRotationDegrees = (float) (angle - 90 + 180);
double degreeCallBack = -(90 - (pointerShouldRotationDegrees - 180));
this.angleForUser = degreeCallBack;
Log.i(TAG, "4 getRockerPositionPoint: 角度 :" + angle + " 应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
invalidate();
}
if (this.angleForUser == -180) {
this.angleForUser = 0;
}
if (mAngleChangedListener != null) {
mAngleChangedListener.onAngleChanged(this.angleForUser);
}
}
/**
* 弧度转角度
*
* @param radian 弧度
* @return 角度[0, 360)
*/
private double radian2Angle(double radian) {
double tmp = Math.round(radian / Math.PI * 180);
return tmp >= 0 ? tmp : 360 + tmp;
}
public void setClockNormal() {
acurrentWheelBitmap = aWheelBitmap;
invalidate();
}
public void setClockPressed() {
acurrentWheelBitmap = aWheelBitmapPressed;
invalidate();
}
public Bitmap getChildNormalBitmap() {
Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_normal, null);
//return drawableToBitmap(drawable, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
return ((BitmapDrawable) drawable).getBitmap();
}
public Bitmap getChildPressedBitmap() {
Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_pressed, null);
return ((BitmapDrawable) drawable).getBitmap();
}
/**
* 表盘参数复位
*/
public void reset() {
pointerShouldRotationDegrees = pointerStartDegrees;
this.angleForUser = 0;
this.isChecked = false;
invalidate();
if (mAngleChangedListener != null) {
mAngleChangedListener.onAngleTouchUp(this.angleForUser, this.isChecked);
}
setClockNormal();
}
public double angleForUser = 0;
private OnAngleChangedListener mAngleChangedListener;
public void setAngleChangedListener(OnAngleChangedListener angleChangedListener) {
mAngleChangedListener = angleChangedListener;
}
public void addAuto() {
this.currentRealAngle = currentRealAngle + 10;
double angle = this.currentRealAngle;
Log.e(TAG, "add auto " + angle);
setAngel(angle);
}
public void reduceAuto() {
this.currentRealAngle = currentRealAngle - 10;
double angle = this.currentRealAngle;
setAngel(angle);
}
}
半圆表盘
package com.kongqw.rockerlibrary.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import androidx.core.content.res.ResourcesCompat;
import com.test.demo.R;
public class ClockwidgetHalf extends View {
private final String TAG = "ClickWidget";
private boolean isChecked = false;
private float mCenterPointX;
private float mCenterPointY;
private Paint paint;
private int height;
private int width;
private int radius;
static final float[] COMMON = {-90, 180};
private float horizontalX = 0;
private float horizontalY = 0;
private final float pointerStartDegrees = COMMON[0];
private double currentRealAngle = COMMON[1];
private float pointerShouldRotationDegrees = pointerStartDegrees;
private int centerWheelWidth;
private int centerWheelHeight;
public ClockwidgetHalf(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private Bitmap aWheelBitmap, aWheelBitmapPressed;
private Bitmap acurrentWheelBitmap;
private Bitmap pointerBitmap;
private void init() {
// Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_normal, null);
// aWheelBitmap = drawableToBitmap(drawable, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
// Drawable drawableApressed = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_pressed, null);
// aWheelBitmapPressed = drawableToBitmap(drawableApressed, drawableApressed.getIntrinsicWidth(), drawableApressed.getIntrinsicHeight());
aWheelBitmap = getChildNormalBitmap();
aWheelBitmapPressed = getChildPressedBitmap();
acurrentWheelBitmap = aWheelBitmap;
centerWheelWidth = acurrentWheelBitmap.getWidth();
centerWheelHeight = acurrentWheelBitmap.getHeight();
Drawable drawablePointer = ResourcesCompat.getDrawable(getResources(), R.mipmap.ic_wheel_pointer, null);
pointerBitmap = drawableToBitmap(drawablePointer, (int) (drawablePointer.getIntrinsicHeight() * 0.6), (int) (drawablePointer.getIntrinsicHeight() * 0.6));
paint = new Paint();
paint.setColor(getResources().getColor(R.color.black));
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
paint.setTextSize(100);
}
/**
* 安卓drawable转bitmap 并控制大小
*
* @param drawable
* @param width
* @param height
* @return
*/
public static Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
height = getMeasuredHeight();
width = getMeasuredWidth();
radius = (int) (Math.min(height, width) * 0.2);
mainBgRadius = (int) (radius * 1.2);
mCenterPointX = ((float) width / 2);
mCenterPointY = ((float) height / 2);
horizontalY = mCenterPointY;
mCenterPoint = new Point((int) mCenterPointX, (int) mCenterPointY);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawMainBg(canvas);
drawWheel(canvas);
drawLineAndText(canvas);
drawPointer(canvas);
}
private void drawWheel(Canvas canvas) {
if (acurrentWheelBitmap != null) {
int bitmapWidth = acurrentWheelBitmap.getWidth();
int bitmapHeight = acurrentWheelBitmap.getHeight();
int viewWidth = getWidth();
int viewHeight = getHeight();
int x = (viewWidth - bitmapWidth) / 2;
int y = (viewHeight - bitmapHeight) / 2;
canvas.save(); // 保存绘图状态
canvas.rotate(0, x + bitmapWidth / 2, y + bitmapHeight / 2); // 旋转绘图坐标系
canvas.drawBitmap(acurrentWheelBitmap, x, y, null); // 绘制Bitmap
canvas.restore(); // 恢复绘图状态
}
}
/**
* 绘制指针
*
* @param canvas
*/
private void drawPointer(Canvas canvas) {
int bitmapWidth = pointerBitmap.getWidth();
int bitmapHeight = pointerBitmap.getHeight();
int x = (int) (mCenterPointX - (bitmapWidth / 2));
int y = (int) (mCenterPointY - radius);
canvas.save(); // 保存绘图状态
canvas.rotate(pointerShouldRotationDegrees, mCenterPointX, mCenterPointY); // 旋转绘图坐标系
canvas.drawBitmap(pointerBitmap, x, y, null); // 绘制Bitmap
canvas.restore(); // 恢复绘图状态
}
/**
* 绘制刻度和文字
*
* @param canvas
*/
private void drawLineAndText(Canvas canvas) {
drawScale2(canvas);
drawScaleText(canvas);
}
float hourScaleHeight = 30;
float minuteScaleHeight = 20;
private void drawScale2(Canvas canvas) {
paint.reset();
paint.setAntiAlias(true);
paint.setColor(getResources().getColor(R.color.kedu_color));
paint.setStrokeWidth(2);
paint.setTextSize(20);
float cy = mCenterPointY;
float cx = mCenterPointX;
float radius = mainBgRadius;
for (int i = 0; i < 60; i++) {
double a = i * 6 * Math.PI / 180;
//刻度我们只绘制前30个
if (i <= 30) {
continue;
}
float startX = (float) (cx + Math.sin(a) * radius);
float startY = (float) (cy - Math.cos(a) * radius);
if (i % 5 == 0) {
float endX = (float) (cx + Math.sin(a) * (radius - hourScaleHeight));
float endY = (float) (cy - Math.cos(a) * (radius - hourScaleHeight));
canvas.drawLine(startX, startY, endX, endY, paint);
} else {
float endX = (float) (cx + Math.sin(a) * (radius - minuteScaleHeight));
float endY = (float) (cy - Math.cos(a) * (radius - minuteScaleHeight));
canvas.drawLine(startX, startY, endX, endY, paint);
}
}
}
String[] hourTexts = {"","", "", "", "", "", "-90", "-60", "-30", "0", "30", "60", "90"};
private void drawScaleText(Canvas canvas) {
float cy = mCenterPointY;
float cx = mCenterPointX;
float radius = mainBgRadius;
paint.reset();
paint.setTextSize(20);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
paint.setColor(getResources().getColor(R.color.color_wheel_text));
//文字是从宽度的中间绘制的
paint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float ascent = fontMetrics.ascent;
float descent = fontMetrics.descent;
for (int i = 0; i < 13; i++) {
float textWidth = paint.measureText(hourTexts[i]);
double a = i * 30 * Math.PI / 180;
//hourScaleHeight刻度的长度
//cx + sina * (radius - hourScaleHeight) 就是刻度点的x坐标,也就是绘制文字的中心点,再减少文字宽度的一半就
//可以不遮挡了
//原来的 float baseX = (float) (cx + Math.sin(a) * (radius - hourScaleHeight - textWidth / 2));
float baseX = (float) (cx + Math.sin(a) * (radius + hourScaleHeight - textWidth / 2));
//cy - cosa * (radius - hourScaleHeight) 就是刻度点的y坐标,cos在圆的上半部分是正值,下半部分是负值
//在左右圆是0,光减刻度是不行的,还要减ascent,在12点方向需要减,6点方向
// 原来的 float baseY = (float) (cy - Math.cos(a) * (radius - hourScaleHeight - Math.abs(ascent * Math.cos(a))));
float baseY = (float) (cy - Math.cos(a) * (radius + hourScaleHeight - Math.abs(ascent * Math.cos(a))));
//baseY == 刻度的y显示会不对齐,所以用中心
//baseY 向上移动到文字的中心,middle = (|ascent| + descent)/2
//baseY 到 middle的距离就是 middle - descent
baseY += ((Math.abs(ascent) - descent) / 2f);
canvas.drawText(hourTexts[i], baseX, baseY, paint);
}
}
private int mainBgRadius = 0;
private void drawMainBg(Canvas canvas) {
paint.reset();
paint.setAntiAlias(true);
paint.setColor(getResources().getColor(R.color.color_wheel_bg));
// canvas.drawCircle(mCenterPointX, mCenterPointY, mainBgRadius, paint);
float startX = mCenterPointX - mainBgRadius;
float startY = mCenterPointY - mainBgRadius;
float endX = mCenterPointX + mainBgRadius;
float endY = mCenterPointY + mainBgRadius;
canvas.drawArc(startX, startY, endX, endY, 90, 180, true, paint);
}
private Point mCenterPoint;
@Override
public boolean onTouchEvent(MotionEvent event) {
float cWheelStartX = (mCenterPointX - (centerWheelWidth / 2));
float cWheelStartY = (mCenterPointY - (centerWheelHeight / 2));
float cWheelEndX = (mCenterPointX + (centerWheelWidth / 2));
float cWheelEndY = (mCenterPointY + (centerWheelHeight / 2));
float moveX = event.getX();
float moveY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:// 按下
// 回调 开始
if (moveX > cWheelStartX && moveX < cWheelEndX &&
moveY > cWheelStartY && moveY < cWheelEndY) {
isChecked = !isChecked;
if (isChecked) {
setClockPressed();
} else {
setClockNormal();
}
}
if (mAngleChangedListener != null) {
mAngleChangedListener.onAngleCheck(isChecked);
}
break;
case MotionEvent.ACTION_MOVE:// 移动
if (moveX < cWheelStartX || moveX > cWheelEndX || moveY > cWheelEndY || moveY < cWheelStartY) {
if (isChecked) {
getRockerPositionPoint(mCenterPoint, new Point((int) moveX, (int) moveY), pointerBitmap.getWidth(), mainBgRadius);
}
}
break;
case MotionEvent.ACTION_UP:// 抬起
if (moveX < cWheelStartX || moveX > cWheelEndX || moveY > cWheelEndY || moveY < cWheelStartY) {
if (mAngleChangedListener != null) {
mAngleChangedListener.onAngleTouchUp(this.angleForUser, isChecked);
}
}
break;
case MotionEvent.ACTION_CANCEL:// 移出区域
break;
}
return true;
}
/**
* 获取摇杆实际要显示的位置(点)
*
* @param centerPoint 中心点
* @param touchPoint 触摸点
* @param regionRadius 摇杆可活动区域半径
* @param rockerRadius 摇杆半径
* @return 摇杆实际显示的位置(点)
*/
private Point getRockerPositionPoint(Point centerPoint, Point touchPoint, float regionRadius, float rockerRadius) {
// 两点在X轴的距离
float lenX = (float) (touchPoint.x - centerPoint.x);
// 两点在Y轴距离
float lenY = (float) (touchPoint.y - centerPoint.y);
// 两点距离
float lenXY = (float) Math.sqrt((double) (lenX * lenX + lenY * lenY));
// 计算弧度
double radian = Math.acos(lenX / lenXY) * (touchPoint.y < centerPoint.y ? -1 : 1);
// 计算角度
double angle = radian2Angle(radian);
currentRealAngle = angle;
Log.e(TAG, "currentRealAngle = " + currentRealAngle);
setAngel(angle);
if (lenXY + rockerRadius <= regionRadius) { // 触摸位置在可活动范围内
return touchPoint;
} else { // 触摸位置在可活动范围以外
// 计算要显示的位置
int showPointX = (int) (centerPoint.x + (regionRadius - rockerRadius) * Math.cos(radian));
int showPointY = (int) (centerPoint.y + (regionRadius - rockerRadius) * Math.sin(radian));
return new Point(showPointX, showPointY);
}
}
private void setAngel(double angle) {
if (angle >= 180 && angle <= 270) {//使用
double degreeCallBack = (180 - (360 - angle));
pointerShouldRotationDegrees = (float) (pointerStartDegrees + degreeCallBack);
this.angleForUser = degreeCallBack;
Log.i(TAG, "1 getRockerPositionPoint: 角度 :" + angle + " 应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
invalidate();
}else if(angle>=90 && angle<=180){
pointerShouldRotationDegrees = (float) (angle - 90 + 180);//使用
double degreeCallBack = -(90 - (pointerShouldRotationDegrees - 180));
this.angleForUser = degreeCallBack;
Log.i(TAG, "4 getRockerPositionPoint: 角度 :" + angle + " 应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
invalidate();
}
if (this.angleForUser == -180) {
this.angleForUser = 0;
}
if (mAngleChangedListener != null) {
mAngleChangedListener.onAngleChanged(this.angleForUser);
}
}
/**
* 弧度转角度
*
* @param radian 弧度
* @return 角度[0, 360)
*/
private double radian2Angle(double radian) {
double tmp = Math.round(radian / Math.PI * 180);
return tmp >= 0 ? tmp : 360 + tmp;
}
public void setClockNormal() {
acurrentWheelBitmap = aWheelBitmap;
invalidate();
}
public void setClockPressed() {
acurrentWheelBitmap = aWheelBitmapPressed;
invalidate();
}
public Bitmap getChildNormalBitmap() {
Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_normal, null);
return ((BitmapDrawable) drawable).getBitmap();
}
public Bitmap getChildPressedBitmap() {
Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_pressed, null);
return ((BitmapDrawable) drawable).getBitmap();
}
/**
* 表盘参数复位
*/
public void reset() {
pointerShouldRotationDegrees = pointerStartDegrees;
this.angleForUser = 0;
this.isChecked = false;
invalidate();
if (mAngleChangedListener != null) {
mAngleChangedListener.onAngleTouchUp(this.angleForUser, this.isChecked);
}
setClockNormal();
}
public double angleForUser = 0;
private OnAngleChangedListener mAngleChangedListener;
public void setAngleChangedListener(OnAngleChangedListener angleChangedListener) {
mAngleChangedListener = angleChangedListener;
}
public void addAuto() {
this.currentRealAngle = currentRealAngle + 10;
double angle = this.currentRealAngle;
setAngel(angle);
}
public void reduceAuto() {
this.currentRealAngle = currentRealAngle - 10;
double angle = this.currentRealAngle;
setAngel(angle);
}
}