效果图预览
1. 分析
1. 绘制中间数字
2. 绘制带刻度的圆环 考虑分成若干等份
3. 绘制渐变圆环 需要用到渐变相关属性
4. 动画处理的同时需要考虑时时计算角度
5. 圆环开关控制按钮的波动范围处理
2. 实现原理
1. 绘制文字这个简单
2. 绘制刻度圆环 分等份 用canvas.drawArc画若干圆环
3. 绘制渐变圆环,需要用到Paint的setShader方法设置渐变颜色
4. 属性动画处理
3. 初始化一些东西 初始化一般我放在onSizeChanged方法中
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mCenterX = w / 2;
mCenterY = h / 2;
int[] colors = {Color.RED,Color.GREEN,Color.YELLOW};
mSweepGradient = new SweepGradient(mCenterX,mCenterY, colors,null);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ring_dot);
mBitmap = conversionBitmap(mBitmap,dp2px(30),dp2px(30));
mMinValidateTouchArcRadius = (int) (mCircleRadius - mBitmap.getWidth() / 2 * 1.5);
mMaxValidateTouchArcRadius = (int) mCircleRadius;
}
4. 绘制中间数字和带刻度线的圆环
/**
* 画带刻度线的圆环
*/
private void drawScaleRing(Canvas canvas) {
float margin = mRingRadius - dp20;
float left = mCenterX - margin;
float top = mCenterY - margin;
float right = left + 2 * margin;
float bottom = top + 2 * margin;
mScaleReacF.set(left,top,right,bottom);
// 等分成360 / 6 = 60份 保证间距一样的
for (int i = 0; i < sweepAngle / 6; i++) {
canvas.drawArc(mScaleReacF, startAngle + i * 6, 1f, false, mScaleRingPaint);
}
}
/**
* 画中间的数字
*/
private void drawMidNum(Canvas canvas) {
String value = String.valueOf(mStepNum);
//测量文字的宽高
mTextPaint.getTextBounds(value, 0, value.length(), mTextRect);
int width = mTextRect.width();//文字宽
int height = mTextRect.height();//文字高
canvas.drawText(value, mCenterX - width / 2, mCenterY + height / 2, mTextPaint);
}
5. 绘制带渐变色的进度圆环
/**
* 画进度圆环
*/
private void drawRingProgress(Canvas canvas) {
canvas.save();
//canvas.rotate(-90);
float left = mCenterX - mRingRadius;
float top = mCenterY - mRingRadius;
float right = left + 2 * mRingRadius;
float bottom = top + 2 * mRingRadius;
mProgressReacF.set(left,top,right,bottom);
//设置渐变颜色
mProgressPaint.setShader(mSweepGradient);
float sweepAngle = mCurrentAngle;
//绘制圆环 startAngle开始角度 sweepAngle扫瞄的角度
canvas.drawArc(mProgressReacF, startAngle, sweepAngle, false, mProgressPaint);
canvas.restore();
}
6. 进度条动画和数字处理
//加载进度条动画
public void setProgressAnimation(float last, float currentProgress, long duration) {
mCurrentAngle = (currentProgress / mMaxProgress) * sweepAngle;
ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, mCurrentAngle);
progressAnimator.setDuration(duration);
progressAnimator.setTarget(mCurrentAngle);
progressAnimator.setInterpolator(new AccelerateInterpolator());
progressAnimator.start();
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
/**每次要绘制的圆弧角度**/
mCurrentAngle = (float) animation.getAnimatedValue();
//根据当前进度和总进度计算当前步数
mStepNum = (int) (mCurrentAngle / sweepAngle * mMaxStepNum);
postInvalidate();
}
});
}
7. 触摸事件处理
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
if (mIsShowControlIcon && (event.getAction() == MotionEvent.ACTION_MOVE || isTouchArc(x, y))) {
// 通过当前触摸点搞到cos角度值
float cos = computeCos(x, y);
// 通过反三角函数获得角度值
double angle;
if (x < mCenterX) { // 滑动超过180度
angle = Math.PI * RADIAN + Math.acos(cos) * RADIAN;
} else { // 没有超过180度
angle = Math.PI * RADIAN - Math.acos(cos) * RADIAN;
}
if (mCurrentAngle > 270 && angle < 90) {
mCurrentAngle = 360;
cos = -1;
} else if (mCurrentAngle < 90 && angle > 270) {
mCurrentAngle = 0;
cos = -1;
} else {
mCurrentAngle = (float) angle;
}
mCurrentProgress = getSelectedValue();
invalidate();
return true;
} else {
return super.onTouchEvent(event);
}
}
private int getSelectedValue() {
return Math.round(mMaxProgress * (mCurrentAngle / sweepAngle));
}
/**
* 按下时判断按下的点是否按在圆边范围内
* @param x
* @param y
*/
private boolean isTouchArc(int x, int y) {
double d = getTouchRadius(x, y);
return d >= mMinValidateTouchArcRadius && d <= mMaxValidateTouchArcRadius;
}
/**
* 计算某点到圆点的距离
* @param x
* @param y
*/
private double getTouchRadius(int x, int y) {
int cx = x - mCenterX;
int cy = y - mCenterY;
return Math.hypot(cx, cy);
}
/**
* 拿到倾斜的cos值
*/
private float computeCos(float x, float y) {
float width = x - mCenterX;
float height = y - mCenterY;
float slope = (float) Math.sqrt(width * width + height * height);
return height / slope;
}
7. 项目源代码下载
后面统一提供源代码下载链接
8. 联系方式
QQ:1509815887