package com.example.mcircleseekbar;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 圆形进度条,支持任意角度开始绘制,支持渐变效果绘制
* <p></p>
* 上午11:12:08
*
* @author ZH-SW-Mengyf
* @version 1.0.0
*/
public class CircleSeekBar extends View{
private Context mContext;
/** 获取控件宽 */
private int mWidth;
/** 获取控件高 */
private int mHeight;
/** x轴中心点 */
private int mCx;
/** y轴中心点 */
private int mCy;
/** 圆半径 */
private float mCRadius;
//
/** 画图层 */
private RectF mRectF = new RectF();
/** 圆画笔 */
private Paint mCirclePaint;
/** 起始角度 */
private int mStartAngle = 270;
/** 角度范围 */
private float mSweepAngle = 0;
/** 画笔的宽度 */
private int mStrokeWidth = 5;
//
/** 背景圆画笔 */
private Paint mBgCirclePaint;
/** 当前进度值 */
private long mProgress = 0;
/** 进度最大值 */
private long mMax = 100;
/** 圆大小 */
private int mCSize;
/** 正常情况下是,宽高除以2,得到半径,但是需要拖拽,所以不能从边线开始,所以除的要比2大才行 */
private float mCRadiusPercent = 2.2f;
/** 托快 */
private Bitmap mThumbBitmap;
/** 绘制托块左侧位置 */
private float mThumbLeft;
/** 绘制托块顶部位置 */
private float mThumbTop;
/** 托块宽 */
private int mThumbWidth;
/** 托块高 */
private int mThumbHeight;
// TODO 用于渐变效果的
/** 圆形颜色数组 */
private int[] mColors = new int[] { Color.parseColor(START_COLOR),
Color.parseColor(ONE_COLOR), Color.parseColor(TWO_COLOR),
Color.parseColor(THREE_COLOR), Color.parseColor(END_COLOR) };
/** 环形渐变渲染 */
private SweepGradient mSweepGradient;
/** 矩阵 用于旋转 */
private Matrix mMatrix;
/** 进度条开始颜色 */
private static final String START_COLOR = "#ff001E1E";
/** 进度条开始3/4颜色 */
private static final String THREE_COLOR = "#ff005D5D";
/** 进度条开始2/4颜色 */
private static final String TWO_COLOR = "#ff004444";
/** 进度条开始1/4颜色 */
private static final String ONE_COLOR = "#ff002B2B";
/** 进度条结束颜色 */
private static final String END_COLOR = "#ff00FFFF";
/**
* @param context
* @param attrs
* @param defStyleAttr
*/
public CircleSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* @param context
* @param attrs
*/
public CircleSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
/**
* @param context
*/
public CircleSeekBar(Context context) {
super(context);
init(context);
}
private void init(Context context){
mContext = context;
initCirclePaint();
initBgCirclePaint();
initThumb();
}
/**
* 初始化圆画笔
*/
private void initCirclePaint() {
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setStyle(Paint.Style.STROKE);
mCirclePaint.setStrokeWidth(mStrokeWidth);
mCirclePaint.setColor(Color.BLACK);
mMatrix = new Matrix();
}
/**
* 初始化背景圆画笔
*/
private void initBgCirclePaint() {
mBgCirclePaint = new Paint();
mBgCirclePaint.setAntiAlias(true);
mBgCirclePaint.setStyle(Paint.Style.STROKE);
mBgCirclePaint.setStrokeWidth(mStrokeWidth);
mBgCirclePaint.setColor(Color.GRAY);
}
/**
* 初始化托块
*/
private void initThumb() {
setThumbUp();
}
/**
* 设置按下和抬起的效果
*/
private void setThumb(boolean up) {
if (up) {
mThumbBitmap = BitmapFactory.decodeResource(mContext.getResources(),R.drawable.thumb_n);
}else{
mThumbBitmap = BitmapFactory.decodeResource(mContext.getResources(),R.drawable.thumb_p);
}
mThumbWidth = mThumbBitmap.getWidth();
mThumbHeight = mThumbBitmap.getHeight();
invalidate();
}
/**
* 设置按下时候的托快效果,按下的时候必须是在拖动范围内
* @param eventX
* @param eventY
* @param up
*/
private void setThumbDown(float eventX,float eventY) {
if (!isEffectiveArea(eventX, eventY)) {
return;
}
setThumb(false);
}
/**
* 设置托快抬起效果,不管是否在范围都应该抬起
*/
private void setThumbUp() {
setThumb(true);
}
@SuppressLint("DrawAllocation")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getWidth();
mHeight = getHeight();
mCx = mWidth / 2;
mCy = mHeight / 2;
mCSize = (mWidth > mHeight) ? mHeight : mWidth;
mCRadius = mCSize / mCRadiusPercent - mStrokeWidth;
float left = mCx - mCRadius;
float right = mCx + mCRadius;
float top = mCy - mCRadius;
float bottom = mCy + mCRadius;
mRectF.set(left, top, right, bottom);
angleToXY(mSweepAngle);
//渐变色
mSweepGradient = new SweepGradient(mCx, mCy,mColors, null);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawCircle(canvas);
// drawShadeCircle(canvas);
//绘制托快
canvas.drawBitmap(mThumbBitmap, mThumbLeft, mThumbTop, null);
canvas.restore();
}
/**
* 绘制圆形进度
* @param canvas 画布
*/
private void drawCircle(Canvas canvas) {
canvas.drawArc(mRectF, 0, 360, false, mBgCirclePaint);
canvas.drawArc(mRectF, mStartAngle, mSweepAngle, false, mCirclePaint);
}
/**
* 绘制渐变型的圆使用的
* @param canvas 画布
*/
private void drawShadeCircle(Canvas canvas) {
mMatrix.setRotate(mSweepAngle + mStartAngle, mCx, mCy);
mSweepGradient.setLocalMatrix(mMatrix);
mCirclePaint.setShader(mSweepGradient);
canvas.drawArc(mRectF, 0, 360, false, mCirclePaint);
canvas.drawArc(mRectF, mStartAngle, mSweepAngle, false, mCirclePaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
moved(eventX, eventY);
setThumbDown(eventX, eventY);
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStartTrackingTouch(this);
}
break;
case MotionEvent.ACTION_MOVE:
moved(eventX, eventY);
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(this, mProgress,true);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
moved(eventX, eventY);
setThumbUp();
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(this,mProgress, false);
mOnSeekBarChangeListener.onStopTrackingTouch(this);
}
break;
default:
break;
}
return true;
}
/**
* 设置手动滑动时候的进度
* @param x
* @param y
* @param up
*/
private void moved(float eventX, float eventY) {
if (!isEffectiveArea(eventX, eventY)) {
return;
}
double radian = Math.atan2(eventY - mCy, eventX - mCx);
if (radian < 0) {
radian = radian + 2 * Math.PI;
}
float angle = Math.round(Math.toDegrees(radian)) - mStartAngle;
mSweepAngle = getLastAngle(angle);
//通过角度获取进度值
mProgress = (long) (mMax * mSweepAngle / 360);
//通过角度得到xy坐标
angleToXY(mSweepAngle);
invalidate();
}
/**
* 获取最后调整的角度,因为起始角度的不同
* @param angle
* @return
*/
private float getLastAngle(float angle) {
//设置起始角度的关键代码,正常情况下Math.round(Math.toDegrees(radian))就可以了。
float lastAngle = 0;
if (angle < 0) {
lastAngle = (360 - mStartAngle) + (mStartAngle - Math.abs(angle));
} else {
lastAngle = angle;
}
return lastAngle;
}
/**
* 判断按下点是否在有效区域
* @param eventX
* @param eventY
* @return
*/
private boolean isEffectiveArea(float eventX, float eventY) {
boolean result = false;
double distance = Math.sqrt(Math.pow(eventX - mCx, 2) + Math.pow(eventY - mCy, 2));
//弧线外面
boolean isCircleOuter = distance <= mCSize;
//弧线里面,如果想设置里面范围都可以拖动,可以(mCRadius- mCRadius)
boolean isCircleInner = distance >= (mCRadius - 40);
if (isCircleOuter && isCircleInner) {
result = true;
}
return result;
}
/**
* 通过角度得到x,y轴,用于托快上使用
* @param angle
*/
private void angleToXY(float angle) {
double radian = Math.toRadians(angle + mStartAngle);
double x = mCx + mCRadius * Math.cos(radian);
double y = mCy + mCRadius * Math.sin(radian);
mThumbLeft = (float) (x - mThumbWidth / 2);
mThumbTop = (float) (y - mThumbHeight / 2);
}
/**
* 设置当前进度
*/
public synchronized void setProgress(long progress) {
if (progress > mMax) {
progress = mMax;
}
if (progress < 0) {
progress = 0;
}
if (mMax == 0) {
return;
}
mProgress = progress;
//当前进度百分比
mSweepAngle = (mProgress * 360 / mMax);
angleToXY(mSweepAngle);
invalidate();
}
/**
* 获取当前进度
*/
public synchronized long getProgress() {
return mProgress;
}
/**
* 设置最大值
*/
public synchronized void setMax(long max) {
if (max < 0) {
max = 0;
}
mMax = max;
invalidate();
}
/**
* 获取最大值
*/
public synchronized long getMax() {
return mMax;
}
/**
* 设置起始角度,0-360度都可以,默认12点方向是270度,3点方向是0度(360度),6点方向90度,9点方向180度
* @param startAngle
*/
public void setStartAngle(int startAngle) {
mStartAngle = startAngle;
invalidate();
}
public interface OnSeekBarChangeListener {
void onProgressChanged(CircleSeekBar circleSeekBar,long progress,boolean fromUser);
void onStartTrackingTouch(CircleSeekBar circleSeekBar);
void onStopTrackingTouch(CircleSeekBar circleSeekBar);
}
private OnSeekBarChangeListener mOnSeekBarChangeListener;
/**
* 设置进度监听
* @param l
*/
public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
mOnSeekBarChangeListener = l;
}
}
可拖拽圆形进度条CircleSeekBar
最新推荐文章于 2021-12-30 11:08:46 发布