Android 手势移动 圆形进度条
首先明确:在Android中坐标系方向为 向右X轴正方向,向下Y轴正方向。
确定自定义view大小(圆弧所在矩形的大小)
mCRadius: 圆弧宽度半径
mOval = new RectF(getPaddingLeft() + mCRadius, getPaddingTop() + mCRadius,
getWidth() - getPaddingRight() - mCRadius,
getHeight() - getPaddingBottom() - mCRadius);
圆形进度条,主要画圆弧,先画轨迹弧形,然后是进度弧形。
画轨迹:360°圆弧,270°(有缺口)的圆弧。
isFilled: 填充模式
if (isFilled) {
trackPaint.setStyle(Paint.Style.FILL);
} else {
trackPaint.setStyle(Paint.Style.STROKE);
}
if (circleBroken) {
canvas.drawArc(mOval, 135, 270, isFilled, trackPaint);
} else {
canvas.drawArc(mOval, 90, 360, isFilled, trackPaint);
}
画进度:偏移进度 * (270°或者360°)/ 100 = 偏移角度
if (isFilled) {
progressPaint.setStyle(Paint.Style.FILL);
} else {
progressPaint.setStyle(Paint.Style.STROKE);
}
if (circleBroken) {
canvas.drawArc(mOval, 135 + mStartProgress * 2.7f, (moveProgress - mStartProgress) * 2.7f, isFilled, progressPaint);
} else {
canvas.drawArc(mOval, 270 + mStartProgress * 3.6f, (moveProgress - mStartProgress) * 3.6f, isFilled, progressPaint);
}
中间部分绘制百分比进度值:
/**
* draw the progress text
*
* @param canvas mCanvas
*/
private void drawProgressText(Canvas canvas) {
if (textVisibility) {
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setTextSize(mProgressTextSize);
mTextPaint.setColor(mProgressTextColor);
mTextPaint.setTextAlign(Paint.Align.CENTER);
// Typeface font = ResourcesCompat.getFont(getContext(),R.font.bahnschrift);
// mTextPaint.setTypeface(font);
String progressText = ((int) moveProgress) + "%";
float x = (getWidth() + getPaddingLeft() - getPaddingRight()) / 2;
float y = (getHeight() + getPaddingTop() - getPaddingBottom() - (mTextPaint.descent() + mTextPaint.ascent())) / 2;
canvas.drawText(progressText, x, y, mTextPaint);
}
}
手势触摸移动,根据手指触摸的坐标,计算与圆心之间的角度,根据
起始角度再计算其偏移角度,最后计算偏移进度。
private void moved(float x,float y){
int startAngle = circleBroken ? 135 : 270;
int endAngle = circleBroken ? 270 : 360;
if (!isEffectiveArea(x,y)){
return;
}
int cx = getWidth() / 2;
int cy = getHeight() / 2;
// y 对边 x 邻边
double radian = Math.atan2(y - cy,x - cx);
if (radian < 0) {
radian = radian + 2 * Math.PI; // 小于0的弧度值 转成 正值
}
float angle = Math.round(Math.toDegrees(radian)); // 相对于坐标系的实际角度
// 90度缺口部分 不进行计算
if (circleBroken && angle <= 135 && angle >= 45) return;
float mSweepAngle = getLastAngle(angle,startAngle);
// 加上起始角度的偏移角度
//通过角度得到xy坐标
// angleToXY(mSweepAngle);
//通过角度获取进度值
float mProgress = (100 * mSweepAngle / endAngle);
if (mProgress > 99) mProgress = 100;
setProgress(mProgress);
}
private float getLastAngle(float angle,int startAngel) {
//设置起始角度的关键代码,正常情况下Math.round(Math.toDegrees(radian))就可以了。
float lastAngle;
if (angle < startAngel) {
lastAngle = 360 - startAngel + Math.abs(angle);
} else {
lastAngle = angle - startAngel;
}
return lastAngle;
}
/**
* 计算其触摸点是否在圆弧可拖动范围内
*/
private boolean isEffectiveArea(float x,float y){
boolean result = false;
int cx = getWidth() / 2;
int cy = getHeight() / 2;
double distance = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2));
//弧线外面
boolean isCircleOuter = distance <= getWidth();
//弧线里面,如果想设置里面范围都可以拖动,可以 0
boolean isCircleInner = distance >= (cx - getPaddingLeft() - mCRadius - 40);
if (isCircleOuter && isCircleInner) {
result = true;
}
return result;
}
根据进度计算坐标值,主要用于托快放置位置。
/**
* 通过角度得到x,y轴,用于托快上使用 // 相对于起始角度的偏移角度
* @param angle
*/
private void angleToXY(float angle) {
// 向右为0,向下为90
float startAngle = circleBroken ? 135 : 270;
// 计算实际角度
float total = 360 - startAngle;
float caculateAngle; // 实际角度值
if (angle < total) {
caculateAngle = angle + startAngle;
} else {
caculateAngle = angle - total;
}
double radian = Math.toRadians(caculateAngle); // 计算实际角度的弧度值
double radius = getWidth() / 2 - getPaddingLeft()- mCRadius; // 减去预留的部分值 mOval.left 得到半径斜边
double x = getWidth() / 2 + radius * Math.cos(radian); // 邻边 / 斜边
double y = getHeight() / 2 + radius * Math.sin(radian); // 对应边 / 斜边
mThumbLeft = (float) (x - mCRadius);
mThumbTop = (float) (y - mCRadius);
}
private void progressToXY() {
// 通过进度值获取角度
int endAngle = circleBroken ? 270 : 360;
float angle = moveProgress / 100 * endAngle;
angleToXY(angle);
}