package com.hdl.widget; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.SweepGradient; import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_UP; public class HDLArcScaleSeekBar extends View { private static final String TAG = "HDLArcScaleSeekBar"; private static final int DEFAULT_ROUND_BORDER_COLOR = 0xFFEAEAEB; private static final float CIRCLE_ANGLE = 360; // 圆周角 private static final int DEFAULT_EDGE_LENGTH = 200; // 默认宽高 private float mSeekBarWidth; private float mSeekBarHeight; private float mCenterX; // 圆弧 SeekBar 中心点 X private float mCenterY; // 圆弧 SeekBar 中心点 Y private int mMaxValue; // 最大数值 private int mMinValue; // 最小数值 private float mPercent;//进度条占比 private int mProgress;//可以更新的进度条数值 // 画圆弧的画笔 private Paint mRoundPaint; // 正方形的宽高 private int mArcWidth; // 圆弧的半径 private float mArcRadius; // 矩形 private RectF oval; private float mOpenAngle = 90; // 开口的角度大小 0 - 360 // 圆弧的起始角度 private float startAngle = 135; private float endAngle = 45; // 圆弧的经过总范围角度角度 private float sweepAngle = 270; private float mScaleLineHeight; private float mScaleLineHeightOut;//突出长度为线长1/4 private float mScaleLineWidth; private float bgRoundWidth; private float bgRoundPadding; private float mRoundX = 0; private float mLinePadding; //开始颜色 private int mStartColor; //结束颜色 private int mEndColor; //当前颜色 private int mNowColor; // 绘制文字 private Paint mTextPaint; private float mTextSizeBig; private float mTextSizeSmall; // 存放第一条水波Y值 private float[] firstWaterLine; // 第二条 private float[] secondWaterLine; // 画水球的画笔 private Paint waterPaint; // 影响三角函数的初相 private float mWaveMove; // 剪切圆的半径 private int clipRadius = 100; // 波浪固定高度 private int mWaveHeight = 0; //刻度画笔 private Paint linePaint; //刻度总数 private int mLineCount = 100; // 当前进度的刻度线数 private int mTargetCount = 0; //每格刻度的角度 private float mAngle; //是否可以点击 private boolean isClickable = true; //进度单位符号 private String mProgressBarUnitSring = "℃"; //是否显示进度文字 private boolean isProgressTextShow = true; //是否显示离线状态 private Paint offlinePaint; private boolean isOffline = false; private int offlineRadius = 10; // private Bitmap bgBitmap; // private float bgHeight; // private float bgWidtht; public HDLArcScaleSeekBar(Context context) { this(context,null); } public HDLArcScaleSeekBar(Context context, AttributeSet attrs) { super(context, attrs); initData(); // initView(context); initPaint(); } // private void initImageView(){ // bgHeight = clipRadius/2.0f; // bgWidtht = clipRadius*2.9f/2.0f; // bgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_wd_arc_scale_bg); // bgBitmap = resizeImage(bgBitmap, (int)bgWidtht, (int) bgHeight); // // } //初始化UI,可根据业务需求设置默认值。 private void initView(Context context) { } private float getEndAngle(float mAngle){ return 90 - mAngle < 0 ? (90-mAngle +360) : (90-mAngle); } /** * 初始化默认参数 */ private void initData() { mProgress = 16; mPercent = 0; mMaxValue = 35; mMinValue = 16; mStartColor = 0xff495AF4; mEndColor = 0xffAE50A8; mNowColor = 0xff495AF4; changeOpenAngle(); mScaleLineHeight = dp2px(15); mScaleLineHeightOut = mScaleLineHeight/4; mScaleLineWidth = dp2px(2); bgRoundWidth = dp2px(8); bgRoundPadding = dp2px(16); mRoundX = 0; mLinePadding = dp2px(10); mTextSizeBig = getTextSizeDip(30); offlineRadius = dp2px(5); // mTextSizeSmall = getTextSizeDip(20); // mTextSizeSmall = mTextSizeBig - 20; } private void changeOpenAngle(){ sweepAngle = CIRCLE_ANGLE - mOpenAngle; startAngle = 90 + mOpenAngle/2; endAngle = getEndAngle(mOpenAngle/2); } /** * 大小改变,刷新所有重要的参数值 */ private void oncurtainSizeChange() { mRoundX = mScaleLineHeight + bgRoundPadding + mScaleLineHeightOut; clipRadius = (int)(mArcRadius - mRoundX ); int ll = (int) ( sweepAngle * Math.PI * mArcRadius) / 180; mLineCount = (int)(ll/(mScaleLineWidth + mLinePadding)); mAngle = sweepAngle / mLineCount; mWaveHeight = clipRadius / 3 ; mTargetCount = (int)(mPercent * mLineCount); // initImageView(); } /** * 初始化画笔 */ private void initPaint() { // 初始化画笔 linePaint = new Paint(); linePaint.setStrokeWidth(mScaleLineWidth); linePaint.setAntiAlias(true); //背景圆画笔 mRoundPaint = new Paint(); mRoundPaint.setColor(DEFAULT_ROUND_BORDER_COLOR); mRoundPaint.setStrokeWidth(bgRoundWidth); mRoundPaint.setAntiAlias(true); mRoundPaint.setStyle(Paint.Style.STROKE); //进度文字画笔 mTextPaint = new Paint(); mTextPaint.setColor(Color.BLACK); mTextPaint.setAntiAlias(true); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setTextSize(mTextSizeBig); //波浪画笔 waterPaint = new Paint(); waterPaint.setAntiAlias(true); //离线画笔 offlinePaint = new Paint(); offlinePaint.setColor(HDLUtlisXM.DEFAULT_OFFLINE_COLOR); offlinePaint.setAntiAlias(true);//设置抗锯齿 } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int ws = MeasureSpec.getSize(widthMeasureSpec); //取出宽度的确切数值 int wm = MeasureSpec.getMode(widthMeasureSpec); //取出宽度的测量模式 int hs = MeasureSpec.getSize(heightMeasureSpec); //取出高度的确切数值 int hm = MeasureSpec.getMode(heightMeasureSpec); //取出高度的测量模 if (wm == MeasureSpec.UNSPECIFIED) { wm = MeasureSpec.EXACTLY; ws = dp2px(DEFAULT_EDGE_LENGTH); } else if (wm == MeasureSpec.AT_MOST) { wm = MeasureSpec.EXACTLY; ws = Math.min(dp2px(DEFAULT_EDGE_LENGTH), ws); } if (hm == MeasureSpec.UNSPECIFIED) { hm = MeasureSpec.EXACTLY; hs = dp2px(DEFAULT_EDGE_LENGTH); } else if (hm == MeasureSpec.AT_MOST) { hm = MeasureSpec.EXACTLY; hs = Math.min(dp2px(DEFAULT_EDGE_LENGTH), hs); } setMeasuredDimension(MeasureSpec.makeMeasureSpec(ws, wm), MeasureSpec.makeMeasureSpec(hs, hm)); // mSeekBarHeight = hs; // mSeekBarWidth = ws; // mCenterX = mSeekBarWidth/2; // mCenterY = mSeekBarHeight/2; // // 取出最小值 // mArcWidth = Math.min(ws, hs); // oncurtainSizeChange(); } // private int measureSize(int defaultSize,int measureSpec) { // int result = defaultSize; // int specMode = MeasureSpec.getMode(measureSpec); // int specSize = MeasureSpec.getSize(measureSpec); // // if (specMode == MeasureSpec.EXACTLY) { // result = specSize; // } else if (specMode == MeasureSpec.AT_MOST) { // result = Math.min(result, specSize); // } // return result; // } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // 计算在当前大小下,内容应该显示的大小和起始位置 int safeW = w - getPaddingLeft() - getPaddingRight(); int safeH = h - getPaddingTop() - getPaddingBottom(); mSeekBarHeight = safeH; mSeekBarWidth = safeW; // 取出最小值 mArcWidth = Math.min(safeW, safeH); mCenterX = mArcWidth/2; mCenterY = mArcWidth/2; mArcRadius = mArcWidth / 2; firstWaterLine = new float[mArcWidth]; secondWaterLine = new float[mArcWidth]; oncurtainSizeChange(); } //--- 初始化结束 ------------------------------------------------------------------------------- //--- 状态存储 --------------------------------------------------------------------------------- private static final String KEY_PROGRESS_PRESENT = "PRESENTARCSCALE"; // 用于存储和获取当前百分比 @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable("superState", super.onSaveInstanceState()); bundle.putFloat(KEY_PROGRESS_PRESENT, mProgress); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; this.mProgress = bundle.getInt(KEY_PROGRESS_PRESENT); state = bundle.getParcelable("superState"); } if (null != mOnProgressChangeListener) { mOnProgressChangeListener.onProgressChanged(this, this.mProgress, false); } super.onRestoreInstanceState(state); } //--- 状态存储结束 ----------------------------------------------------------------------------- @Override protected void onDraw(Canvas canvas) { // 画刻度线 drawLine(canvas); // 画水波 drawWaterView(canvas); // 画刻度线内的内容 drawText(canvas); //绘制离线遮挡层 if(isOffline){ RectF rectF2 = new RectF(0, 0, mSeekBarWidth, mSeekBarHeight); canvas.drawRoundRect(rectF2, offlineRadius, offlineRadius, offlinePaint); } } /** * 画水球的功能 * * @param canvas */ private void drawWaterView(Canvas canvas) { // y = Asin(wx+b)+h ,w影响周期,A影响振幅,h影响y位置,b为初相; // 将周期定为view总宽度 float mCycleFactorW = (float) (2 * Math.PI / mArcWidth); // 得到第一条波的y值 for (int i = 0; i < mArcWidth; i++) { firstWaterLine[i] = (float) (40 * Math .sin(mCycleFactorW * i + mWaveMove) - mWaveHeight); } // // 得到第一条波的y值 // for (int i = 0; i < mArcWidth; i++) { // secondWaterLine[i] = (float) (45 * Math.sin(mCycleFactorW * i // + mWaveMove + 10) - mWaveHeight); // } // 画圆形背景 RectF roundRectF = new RectF(mRoundX, mRoundX, mArcWidth - mRoundX, mArcWidth - mRoundX); canvas.save(); // 裁剪成圆形区域 Path path = new Path(); waterPaint.setColor(mNowColor); path.reset(); canvas.clipPath(path); path.addArc(roundRectF, 0, CIRCLE_ANGLE); // path.addCircle(mArcWidth / 2, mArcWidth / 2, clipRadius - bgStrokeWidth/2, Path.Direction.CCW); canvas.clipPath(path, android.graphics.Region.Op.REPLACE); // 将坐标系移到底部 canvas.translate(0, mArcRadius + clipRadius); resetmArcProgressBarColor(); for (int i = 0; i < mArcWidth; i++) { canvas.drawLine(i, firstWaterLine[i], i, mArcWidth, waterPaint); } // for (int i = 0; i < mArcWidth; i++) { // canvas.drawLine(i, secondWaterLine[i], i, mArcWidth, waterPaint); // } canvas.restore(); // canvas.drawBitmap(bgBitmap, mSeekBarWidth/2- bgWidtht/2 , mSeekBarHeight- mRoundX-bgHeight, new Paint()); //绘制背景圆 canvas.drawArc(roundRectF, 0, CIRCLE_ANGLE, false, mRoundPaint); } /** * 重置 mArcProgressBarColors 颜色 */ private void resetmArcProgressBarColor() { // LinearGradient gradient = new LinearGradient(mCenterX-100, mSeekBarHeight-mWaveHeight, mCenterX+100, mWaveHeight, mStartColor, mEndColor, Shader.TileMode.MIRROR); LinearGradient gradient = new LinearGradient(mRoundX+30, mSeekBarHeight-mRoundX, mArcWidth - mRoundX-30, mSeekBarHeight - mWaveHeight, mStartColor, mEndColor, Shader.TileMode.MIRROR); waterPaint.setShader(gradient); } /** * 实现画刻度线内的内容 * * @param canvas */ private void drawText(Canvas canvas) { if (isProgressTextShow) { canvas.drawText(String.valueOf(getProgress()) + mProgressBarUnitSring, mCenterX, mCenterY, mTextPaint); } } /** * 实现画刻度线的功能 * * @param canvas */ private void drawLine(final Canvas canvas) { // 保存之前的画布状态 canvas.save(); // 移动画布,实际上是改变坐标系的位置 canvas.translate(mArcRadius, mArcRadius); // 旋转坐标系,需要确定旋转角度 canvas.rotate(mOpenAngle/2); // move = targetAngle/sweepAngle*mArcWidth; for (int i = 0; i <= mLineCount; i++) { if (i <= mTargetCount && mTargetCount != 0) {// 如果累计画过的角度,小于当前有效刻度 float mColorPercent = (float)i / mLineCount; // 计算累计划过的刻度百分比(画过的刻度比上中共进过的刻度) mNowColor = getCurrentColor(mColorPercent, mStartColor, mEndColor); linePaint.setColor(mNowColor); // canvas.drawLine(0, mArcRadius, 0, mArcRadius - mScaleLineHeight, linePaint); if(mTargetCount == i){ canvas.drawLine(0, mArcRadius, 0, mArcRadius - mScaleLineHeight - mScaleLineHeightOut, linePaint); }else { canvas.drawLine(0, mArcRadius - mScaleLineHeightOut, 0, mArcRadius - mScaleLineHeight - mScaleLineHeightOut, linePaint); } } else { linePaint.setColor(DEFAULT_ROUND_BORDER_COLOR); canvas.drawLine(0, mArcRadius - mScaleLineHeightOut, 0, mArcRadius - mScaleLineHeight - mScaleLineHeightOut, linePaint); } canvas.rotate(mAngle); } // 恢复画布状态。 canvas.restore(); } /** * 根据fraction值来计算当前的颜色。 fraction值范围 0f-1f */ private int getCurrentColor(float fraction, int startColor, int endColor) { int redCurrent; int blueCurrent; int greenCurrent; int alphaCurrent; int redStart = Color.red(startColor); int blueStart = Color.blue(startColor); int greenStart = Color.green(startColor); int alphaStart = Color.alpha(startColor); int redEnd = Color.red(endColor); int blueEnd = Color.blue(endColor); int greenEnd = Color.green(endColor); int alphaEnd = Color.alpha(endColor); int redDifference = redEnd - redStart; int blueDifference = blueEnd - blueStart; int greenDifference = greenEnd - greenStart; int alphaDifference = alphaEnd - alphaStart; redCurrent = (int) (redStart + fraction * redDifference); blueCurrent = (int) (blueStart + fraction * blueDifference); greenCurrent = (int) (greenStart + fraction * greenDifference); alphaCurrent = (int) (alphaStart + fraction * alphaDifference); return Color.argb(alphaCurrent, redCurrent, greenCurrent, blueCurrent); } private boolean mCanDrag = false; // 是否允许拖动 private boolean moved = false; // 是否允许 private int lastProgress = -1; @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); if(!isClickable) return false; int action = event.getActionMasked(); switch (action) { case ACTION_DOWN: moved = false; judgeCanDrag(event); if (!mCanDrag) { break; } if (null != mOnProgressChangeListener) { mOnProgressChangeListener.onStartTrackingTouch(this); } break; case ACTION_MOVE: if (!mCanDrag) { break; } setCurrentPercent(event); // 事件回调 if (null != mOnProgressChangeListener && mProgress != lastProgress) { mOnProgressChangeListener.onProgressChanged(this, mProgress, true); lastProgress = mProgress; } moved = true; break; case ACTION_UP: case ACTION_CANCEL: if(!moved){ if (isInArcProgress(event.getX(),event.getY())) { setCurrentPercent(event); } } if(mCanDrag) { if (null != mOnProgressChangeListener) { mOnProgressChangeListener.onStopTrackingTouch(this); } } break; } invalidate(); return true; } // 判断是否允许拖动 private void judgeCanDrag(MotionEvent event) { if(isInArcProgress(event.getX(),event.getY())){ mCanDrag = true; }else { mCanDrag = false; } } private void setCurrentPercent(MotionEvent event) { float currentAngle = getCurrentAngle(event.getX(), event.getY()); //判断是否在符合角度 if((currentAngle < startAngle-10) && (currentAngle > endAngle + 10)){ return; } float diffAngle = currentAngle - startAngle ; if (diffAngle < 0) { if(currentAngle < 90) { diffAngle = (diffAngle + CIRCLE_ANGLE) % CIRCLE_ANGLE; }else { diffAngle = 0; } } float progress = diffAngle / sweepAngle; if (progress < 0) progress = 0; if (progress > 1) progress = 1; mPercent = progress; mProgress = (int) (mPercent * (mMaxValue - mMinValue)) + mMinValue; mTargetCount = (int)(mPercent * mLineCount); } //判断是否在可点击区域 private boolean isInArcProgress(float px ,float py) { if(px < 0 || px > mSeekBarWidth || py < 0 || py > mSeekBarHeight){ return false; }else { if(isInOutArc(px, py)){ return true; }else { return false; } } } //是否在外圆 private boolean isInOutArc(float px ,float py) { if(getDistanceWithCenter(px, py) < (mArcRadius - mRoundX) ){ return false; }else { return true; } } // 计算坐标圆点的距离 private double getDistanceWithCenter(float px, float py) { return Math.sqrt((px - mCenterX) * (px - mCenterX) + (py - mCenterY) * (py - mCenterY)); } // 计算指定位置与内容区域中心点的夹角 private float getCurrentAngle(float px, float py) { float angle = (float) ((Math.atan2(py - mCenterY, px - mCenterX)) * 180 / 3.14f); if (angle < 0) { angle += 360; } return angle; } private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics()); } private float getTextSizeDip(float value) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics()); } /** * mOnProgressChangeListener */ private HDLArcScaleSeekBar.OnProgressChangeListener mOnProgressChangeListener; public void setOnProgressChangeListener(HDLArcScaleSeekBar.OnProgressChangeListener onProgressChangeListener) { mOnProgressChangeListener = onProgressChangeListener; } public interface OnProgressChangeListener { /** * 进度发生变化 * * @param seekBar 拖动条 * @param progress 当前进度数值 * @param isUser 是否是用户操作, true 表示用户拖动, false 表示通过代码设置 */ void onProgressChanged(HDLArcScaleSeekBar seekBar, int progress, boolean isUser); /** * 用户开始拖动 * * @param seekBar 拖动条 */ void onStartTrackingTouch(HDLArcScaleSeekBar seekBar); /** * 用户结束拖动 * * @param seekBar 拖动条 */ void onStopTrackingTouch(HDLArcScaleSeekBar seekBar); } // endregion ----------------------------------------------------------------------------------- //****************************对外接口**************************************************** /** * 设置控件是否可用点击 * @param isClickable */ public void setIsClickable(boolean isClickable) { this.isClickable = isClickable; } /** * 进度文字大小 * @param textSize */ public void setProgressTextSize(int textSize){ mTextSizeBig = getTextSizeDip(textSize); mTextPaint.setTextSize(mTextSizeBig); } /** * 进度文字颜色 * @param textPaintColor */ public void setProgressTextColor(int textPaintColor){ mTextPaint.setColor(textPaintColor); } /** * 设置进度显示值单位 * * @param progressBarUnitSring 进度显示值单位 */ public void setProgressBarUnitSring(String progressBarUnitSring) { mProgressBarUnitSring = progressBarUnitSring; } /** * 设置最大数值 * * @param max 最大数值 */ public void setMaxValue(int max) { mMaxValue = max; } /** * 设置最小数值 * * @param min 最小数值 */ public void setMinValue(int min) { mMinValue = min; } public int getProgress() { return mProgress; } /** * 手动设置刻度盘的值 */ public void setProgress(int progress) { if (progress > mMaxValue) progress = mMaxValue; if (progress < mMinValue) progress = mMinValue; mProgress = progress; mPercent = (progress - mMinValue) * 1.0f / (mMaxValue - mMinValue); mTargetCount = (int)(mPercent * mLineCount); // if (null != mOnProgressChangeListener) { // mOnProgressChangeListener.onProgressChanged(this, progress, false); // } postInvalidate(); } public void setOpenAngle(float openAngle) { mOpenAngle = openAngle; changeOpenAngle(); oncurtainSizeChange(); } public void setScaleLineHeight(float scaleLineHeight) { mScaleLineHeight = scaleLineHeight; mScaleLineHeightOut = mScaleLineHeight/4; oncurtainSizeChange(); } public void setScaleLineWidth(float scaleLineWidth) { mScaleLineWidth = scaleLineWidth; linePaint.setStrokeWidth(mScaleLineWidth); oncurtainSizeChange(); } public void setBgRoundWidth(float bgRoundWidth) { this.bgRoundWidth = bgRoundWidth; mRoundPaint.setStrokeWidth(bgRoundWidth); } public void setBgRoundPadding(float bgRoundPadding) { this.bgRoundPadding = bgRoundPadding; oncurtainSizeChange(); } public void setLinePadding(float linePadding) { this.mLinePadding = linePadding; oncurtainSizeChange(); } public void setStartColor(int startColor) { this.mStartColor = startColor; } public void setEndColor(int endColor) { this.mEndColor = endColor; } public void setProgressTextShow(boolean progressTextShow) { isProgressTextShow = progressTextShow; } /** * 设置离线状态 * @param offline */ public void setOffline(boolean offline) { isOffline = offline; isClickable = !isOffline; postInvalidate(); } public void setInitConfig(){ } }