可拖拽圆形进度条CircleSeekBar

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;
	}

}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值