package com.hdl.widget; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.Point; import android.graphics.RectF; import android.graphics.Region; import android.graphics.SweepGradient; import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import com.hdl.widgetxm.R; 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 HDLArcSeekBar extends View { private static final int DEFAULT_EDGE_LENGTH = 260; // 默认宽高 private static final float CIRCLE_ANGLE = 360; // 圆周角 private static final int DEFAULT_ARC_WIDTH = 15; // 默认宽度 dp private static final float DEFAULT_OPEN_ANGLE = 30; // 开口角度 private static final float DEFAULT_ROTATE_ANGLE = 90; // 旋转角度 private static final int DEFAULT_BORDER_WIDTH = 0; // 默认描边宽度 private static final int DEFAULT_BORDER_COLOR = 0xffffffff; // 默认描边颜色 private static final int DEFAULT_THUMB_COLOR = 0xffffffff; // 拖动按钮颜色 private static final int DEFAULT_THUMB_WIDTH = 5; // 拖动按钮描边宽度 dp private static final int DEFAULT_THUMB_RADIUS = 7; // 拖动按钮半径 dp private static final int DEFAULT_THUMB_SHADOW_RADIUS = 0; // 拖动按钮阴影半径 dp private static final int DEFAULT_THUMB_SHADOW_COLOR = 0xFF000000; // 拖动按钮阴影颜色 private static final int DEFAULT_SHADOW_RADIUS = 0; // 默认阴影半径 dp public static final int THUMB_MODE_STROKE = 0; // 拖动按钮模式 - 描边 public static final int THUMB_MODE_FILL = 1; // 拖动按钮模式 - 填充 public static final int THUMB_MODE_FILL_STROKE = 2; // 拖动按钮模式 - 填充+描边 private static final int DEFAULT_MAX_VALUE = 100; // 默认最大数值 private static final int DEFAULT_MIN_VALUE = 0; // 默认最小数值 private static final String KEY_PROGRESS_PRESENT = "PRESENT"; // 用于存储和获取当前百分比 // 可配置数据 private int[] mArcColors; // Seek 颜色 private float mArcWidth; // Seek 宽度 private float mOpenAngle; // 开口的角度大小 0 - 360 private float mRotateAngle; // 旋转角度 private int mBorderWidth; // 描边宽度 private int mBorderColor; // 描边颜色 private int mThumbColor; // 拖动按钮颜色 private float mThumbWidth; // 拖动按钮宽度 private float mThumbRadius; // 拖动按钮半径 private float mThumbShadowRadius;// 拖动按钮阴影半径 private int mThumbShadowColor;// 拖动按钮阴影颜色 private int mThumbMode; // 拖动按钮模式 private int mShadowRadius; // 阴影半径 private int mMaxValue; // 最大数值 private int mMinValue; // 最小数值 private float mCenterX; // 圆弧 SeekBar 中心点 X private float mCenterY; // 圆弧 SeekBar 中心点 Y private float mThumbX; // 拖动按钮 中心点 X private float mThumbY; // 拖动按钮 中心点 Y private Path mSeekPath; private Path mBorderPath; private Paint mArcPaint; private Paint mThumbPaint; private Paint mBorderPaint; private Paint mShadowPaint; private float[] mTempPos; private float[] mTempTan; private PathMeasure mSeekPathMeasure; private float mProgressPresent = 0; // 当前进度百分比 private boolean mCanDrag = false; // 是否允许拖动 private boolean mAllowTouchSkip = false; // 是否允许越过边界 // private GestureDetector mDetector; private Matrix mInvertMatrix; // 逆向 Matrix, 用于计算触摸坐标和绘制坐标的转换 private Region mArcRegion; // ArcPath的实际区域大小,用于判定单击事件 //***********************新增*********************************** private static final int DEFAULT_THUMB_DISTANCE_MULTIPLE = 5; //点击的坐标离与mThumb圆心坐标距离,小于5*半径,则可以拖动 private static final int DEFAULT_ARC_PROGRESS_BAR_COLOR = 0xFFFFFFFF; // 进度条颜色 private static final String DEFAULT_ARC_PROGRESS_BAR_UNIT_STRING = "%"; // 默认显示单位 // private static final int DEFAULT_DISTANCE_BETWEEN_TEXTPOINT_AND_ARC = 70; private static final int DEFAULT_PADDING = 80; //圆弧自带PADDING值,为了显示进度test文字 private Point mTextPoint; //旋转90度后的坐标 private Paint mTextPaint; //进度文字显示内容 private RectF content; //显示内容区域 private float mSweepAngle = 0; //与开始角度的夹角 private int mArcProgressBarColor; //当前进度圆弧颜色 private Paint mArcProgressBarPaint; //当前进度圆弧画笔 private String mProgressBarUnitSring; //进度单位符号 private double mArcProgressBarRadius; //圆弧的半径 private boolean isClickable = true; private int mTextDefaultDistance = 70; // 进度显示文字坐标与进度圆弧的距离 DEFAULT_DISTANCE_BETWEEN_TEXTPOINT_AND_ARC private int[] mArcProgressBarColors; // 当前进度圆弧渐变颜色数组 // private int mTextColor; // 拖动按钮颜色 //***********************新增*********************************** public HDLArcSeekBar(Context context) { this(context, null); } public HDLArcSeekBar(Context context, AttributeSet attrs) { this(context, attrs, 0); } public HDLArcSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setSaveEnabled(true); setLayerType(LAYER_TYPE_SOFTWARE, null); initAttrs(context, attrs); initData(); initPaint(); } //--- 初始化 ----------------------------------------------------------------------------------- // 初始化各种属性 private void initAttrs(Context context, AttributeSet attrs) { TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HDLArcSeekBar); mArcColors = getArcColors(context, ta); mArcWidth = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_width, dp2px(DEFAULT_ARC_WIDTH)); mOpenAngle = ta.getFloat(R.styleable.HDLArcSeekBar_arc_open_angle, DEFAULT_OPEN_ANGLE); mRotateAngle = ta.getFloat(R.styleable.HDLArcSeekBar_arc_rotate_angle, DEFAULT_ROTATE_ANGLE); mMaxValue = ta.getInt(R.styleable.HDLArcSeekBar_arc_max, DEFAULT_MAX_VALUE); mMinValue = ta.getInt(R.styleable.HDLArcSeekBar_arc_min, DEFAULT_MIN_VALUE); // 如果用户设置的最大值和最小值不合理,则直接按照默认进行处理 if (mMaxValue <= mMinValue) { mMaxValue = DEFAULT_MAX_VALUE; mMinValue = DEFAULT_MIN_VALUE; } int progress = ta.getInt(R.styleable.HDLArcSeekBar_arc_progress, mMinValue); setProgress(progress); mBorderWidth = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_border_width, dp2px(DEFAULT_BORDER_WIDTH)); mBorderColor = ta.getColor(R.styleable.HDLArcSeekBar_arc_border_color, DEFAULT_BORDER_COLOR); mThumbColor = ta.getColor(R.styleable.HDLArcSeekBar_arc_thumb_color, DEFAULT_THUMB_COLOR); mThumbRadius = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_thumb_radius, dp2px(DEFAULT_THUMB_RADIUS)); mThumbShadowRadius = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_thumb_shadow_radius, dp2px(DEFAULT_THUMB_SHADOW_RADIUS)); mThumbShadowColor = ta.getColor(R.styleable.HDLArcSeekBar_arc_thumb_shadow_color, DEFAULT_THUMB_SHADOW_COLOR); mThumbWidth = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_thumb_width, dp2px(DEFAULT_THUMB_WIDTH)); mThumbMode = ta.getInt(R.styleable.HDLArcSeekBar_hdl_arc_thumb_mode, THUMB_MODE_STROKE); mShadowRadius = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_shadow_radius, dp2px(DEFAULT_SHADOW_RADIUS)); mArcProgressBarColor = ta.getColor(R.styleable.HDLArcSeekBar_arc_progress_bar_color, DEFAULT_ARC_PROGRESS_BAR_COLOR); mArcProgressBarColors = new int[]{mArcProgressBarColor,mArcProgressBarColor}; ta.recycle(); } // 获取 Arc 颜色数组 private int[] getArcColors(Context context, TypedArray ta) { int[] ret; int resId = ta.getResourceId(R.styleable.HDLArcSeekBar_arc_colors, 0); if (0 == resId) { resId = R.array.arc_colors_default; } ret = getColorsByArrayResId(context, resId); return ret; } // 根据 resId 获取颜色数组 private int[] getColorsByArrayResId(Context context, int resId) { int[] ret; TypedArray colorArray = context.getResources().obtainTypedArray(resId); ret = new int[colorArray.length()]; for (int i = 0; i < colorArray.length(); i++) { ret[i] = colorArray.getColor(i, 0); } return ret; } // 初始化数据 private void initData() { mSeekPath = new Path(); mBorderPath = new Path(); mSeekPathMeasure = new PathMeasure(); mTempPos = new float[2]; mTempTan = new float[2]; // mDetector = new GestureDetector(getContext(), new OnClickListener()); mInvertMatrix = new Matrix(); mArcRegion = new Region(); mProgressBarUnitSring = DEFAULT_ARC_PROGRESS_BAR_UNIT_STRING; } // 初始化画笔 private void initPaint() { initArcPaint(); initThumbPaint(); initTextPanit(); initProgressBarPaint(); initBorderPaint(); initShadowPaint(); } // 初始化圆弧画笔 private void initArcPaint() { mArcPaint = new Paint(); mArcPaint.setAntiAlias(true); mArcPaint.setStrokeWidth(mArcWidth); mArcPaint.setStyle(Paint.Style.STROKE); mArcPaint.setStrokeCap(Paint.Cap.ROUND); } // 初始化拖动按钮画笔 private void initThumbPaint() { mThumbPaint = new Paint(); mThumbPaint.setAntiAlias(true); mThumbPaint.setColor(mThumbColor); mThumbPaint.setStrokeWidth(mThumbWidth); mThumbPaint.setStrokeCap(Paint.Cap.ROUND); if (mThumbMode == THUMB_MODE_FILL) { mThumbPaint.setStyle(Paint.Style.FILL_AND_STROKE); } else if (mThumbMode == THUMB_MODE_FILL_STROKE) { mThumbPaint.setStyle(Paint.Style.FILL_AND_STROKE); } else { mThumbPaint.setStyle(Paint.Style.STROKE); } mThumbPaint.setTextSize(56); } private float getTextSizeDip(float value) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics()); } // 初始化进度圆弧画笔 private void initProgressBarPaint() { mArcProgressBarPaint = new Paint(); //初始化进度圆弧画笔 mArcProgressBarPaint.setAntiAlias(true); mArcProgressBarPaint.setStyle(Paint.Style.STROKE); mArcProgressBarPaint.setStrokeCap(Paint.Cap.ROUND); mArcProgressBarPaint.setStrokeWidth(mArcWidth - 1); // mArcProgressBarPaint.setColor(mArcProgressBarColor); } // 初始化拖动按钮顶部文字画笔和进度圆弧画笔 private void initTextPanit() { mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setStrokeWidth(1); mTextPaint.setTextSize(getTextSizeDip(12)); mTextPaint.setColor(mThumbColor); mTextPaint.setTextAlign(Paint.Align.CENTER); } // 初始化拖动按钮画笔 private void initBorderPaint() { mBorderPaint = new Paint(); mBorderPaint.setAntiAlias(true); mBorderPaint.setColor(mBorderColor); mBorderPaint.setStrokeWidth(mBorderWidth); mBorderPaint.setStyle(Paint.Style.STROKE); } // 初始化阴影画笔 private void initShadowPaint() { mShadowPaint = new Paint(); mShadowPaint.setAntiAlias(true); mShadowPaint.setStrokeWidth(mBorderWidth); mShadowPaint.setStyle(Paint.Style.FILL_AND_STROKE); } //--- 初始化结束 ------------------------------------------------------------------------------- //--- 状态存储 --------------------------------------------------------------------------------- @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable("superState", super.onSaveInstanceState()); bundle.putFloat(KEY_PROGRESS_PRESENT, mProgressPresent); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; this.mProgressPresent = bundle.getFloat(KEY_PROGRESS_PRESENT); state = bundle.getParcelable("superState"); } if (null != mOnProgressChangeListener) { mOnProgressChangeListener.onProgressChanged(this, getProgress(), false); } super.onRestoreInstanceState(state); } //--- 状态存储结束 ----------------------------------------------------------------------------- @Override protected void onMeasure(int widthMeasureSpec, int 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)); } @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(); float edgeLength, startX, startY; float fix = mArcWidth / 2 + mBorderWidth + mShadowRadius * 2; // 修正距离,画笔宽度的修正 if (safeW < safeH) { // 宽度小于高度,以宽度为准 edgeLength = safeW - fix; startX = getPaddingLeft(); startY = (safeH - safeW) / 2.0f + getPaddingTop(); } else { // 宽度大于高度,以高度为准 edgeLength = safeH - fix; startX = (safeW - safeH) / 2.0f + getPaddingLeft(); startY = getPaddingTop(); } // 得到显示区域和中心的 content = new RectF(startX + fix + DEFAULT_PADDING, startY + fix + DEFAULT_PADDING, startX + edgeLength - DEFAULT_PADDING, startY + edgeLength - DEFAULT_PADDING); mCenterX = content.centerX(); mCenterY = content.centerY(); // 得到路径 mSeekPath.reset(); mSeekPath.addArc(content, mOpenAngle / 2, CIRCLE_ANGLE - mOpenAngle); mSeekPathMeasure.setPath(mSeekPath, false); computeThumbPos(mProgressPresent); mArcProgressBarRadius = getDistanceWithCenter(mThumbX, mThumbY);//计算出圆弧的半径 resetShaderColor(); mInvertMatrix.reset(); mInvertMatrix.preRotate(-mRotateAngle, mCenterX, mCenterY); mArcPaint.getFillPath(mSeekPath, mBorderPath); mBorderPath.close(); mArcRegion.setPath(mBorderPath, new Region(0, 0, w, h)); } /** * 重置 mArcProgressBarColors 颜色 * 2019-8-14 */ private void resetmArcProgressBarColor() { // // 计算渐变数组 // float startPos = (mOpenAngle / 2) / CIRCLE_ANGLE; // float stopPos = (CIRCLE_ANGLE - (mOpenAngle / 2)) / CIRCLE_ANGLE; // int len = mArcProgressBarColors.length - 1; // float distance = (stopPos - startPos) / len; // float pos[] = new float[mArcProgressBarColors.length]; // for (int i = 0; i < mArcProgressBarColors.length; i++) { // pos[i] = startPos + (distance * i); // } // SweepGradient gradient = new SweepGradient(mCenterX, mCenterY, mArcProgressBarColors, pos); SweepGradient gradient = new SweepGradient(mCenterX, mCenterY, mArcProgressBarColors, null); mArcProgressBarPaint.setShader(gradient); } // 重置 shader 颜色 private void resetShaderColor() { // 计算渐变数组 float startPos = (mOpenAngle / 2) / CIRCLE_ANGLE; float stopPos = (CIRCLE_ANGLE - (mOpenAngle / 2)) / CIRCLE_ANGLE; int len = mArcColors.length - 1; float distance = (stopPos - startPos) / len; float pos[] = new float[mArcColors.length]; for (int i = 0; i < mArcColors.length; i++) { pos[i] = startPos + (distance * i); } SweepGradient gradient = new SweepGradient(mCenterX, mCenterY, mArcColors, pos); mArcPaint.setShader(gradient); } // 具体绘制 @Override protected void onDraw(Canvas canvas) { canvas.save(); canvas.drawText(String.valueOf(getProgress()) + mProgressBarUnitSring, mTextPoint.x, mTextPoint.y, mTextPaint); canvas.rotate(mRotateAngle, mCenterX, mCenterY); // mShadowPaint.setShadowLayer(mShadowRadius * 2, 0, 0, getColor()); canvas.drawPath(mBorderPath, mShadowPaint); canvas.drawPath(mSeekPath, mArcPaint); //当前进度 resetmArcProgressBarColor();//渐变颜色 canvas.drawArc(content, mOpenAngle / 2, mSweepAngle, false, mArcProgressBarPaint); if (mBorderWidth > 0) { canvas.drawPath(mBorderPath, mBorderPaint); } if (mThumbShadowRadius > 0) { mThumbPaint.setShadowLayer(mThumbShadowRadius, 0, 0, mThumbShadowColor); canvas.drawCircle(mThumbX, mThumbY, mThumbRadius, mThumbPaint); mThumbPaint.clearShadowLayer(); } canvas.drawCircle(mThumbX, mThumbY, mThumbRadius, mThumbPaint); canvas.restore(); } 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 (null != mOnProgressChangeListener) { mOnProgressChangeListener.onStartTrackingTouch(this); } break; case ACTION_MOVE: if (!mCanDrag) { break; } float tempProgressPresent = getCurrentProgress(event.getX(), event.getY()); if (!mAllowTouchSkip) { // 不允许突变 if (Math.abs(tempProgressPresent - mProgressPresent) > 0.5f) { break; } } // 允许突变 或者非突变 mProgressPresent = tempProgressPresent; computeThumbPos(mProgressPresent); // 事件回调 if (null != mOnProgressChangeListener && getProgress() != lastProgress) { mOnProgressChangeListener.onProgressChanged(this, getProgress(), true); lastProgress = getProgress(); } moved = true; break; case ACTION_UP: case ACTION_CANCEL: if(!moved){ if (isInArcProgress(event.getX(), event.getY())) { // 点击允许突变 mProgressPresent = getCurrentProgress(event.getX(), event.getY()); computeThumbPos(mProgressPresent); } } if (null != mOnProgressChangeListener) { mOnProgressChangeListener.onStopTrackingTouch(this); } break; } // mDetector.onTouchEvent(event); invalidate(); return true; } // 判断是否允许拖动 private void judgeCanDrag(MotionEvent event) { float[] pos = {event.getX(), event.getY()}; mInvertMatrix.mapPoints(pos); if (getDistance(pos[0], pos[1]) <= mThumbRadius * DEFAULT_THUMB_DISTANCE_MULTIPLE) { mCanDrag = true; } else { mCanDrag = false; } } private class OnClickListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { // 判断是否点击在了进度区域 if (!isInArcProgress(e.getX(), e.getY())) { return false; } // 点击允许突变 mProgressPresent = getCurrentProgress(e.getX(), e.getY()); computeThumbPos(mProgressPresent); Log.i("onSingleTapUp","onSingleTapUp"); // // 事件回调 // if (null != mOnProgressChangeListener) { // mOnProgressChangeListener.onProgressChanged(HDLArcSeekBar.this, getProgress(), true); //// mOnProgressChangeListener.onStopTrackingTouch(HDLArcSeekBar.this); // } return true; } } // // 判断该点是否在进度条上面 // private boolean isInArcProgress(float px, float py) { // float[] pos = {px, py}; // mInvertMatrix.mapPoints(pos); // return mArcRegion.contains((int) pos[0], (int) pos[1]); // } /** * 判断该点是否在进度条附近 2019-07-17 * 对比判断该点是否在进度条上面,增大可点击区域 * * @param px 当前坐标x * @param py 当前坐标y * @return */ private boolean isInArcProgress(float px, float py) { float[] pos = {px, py}; mInvertMatrix.mapPoints(pos); double mDistance = getDistanceWithCenter(pos[0], pos[1]); // Log.i("kkk" ,"KmArcProgressBarRadiusmDistance"+mDistance); if ((mDistance >= (mArcProgressBarRadius * 2 / 3)) && (mDistance <= (mArcProgressBarRadius * 4 / 3))) { return true; } else { return false; } } // 获取当前进度理论进度数值 private float getCurrentProgress(float px, float py) { float diffAngle = getDiffAngle(px, py); float progress = diffAngle / (CIRCLE_ANGLE - mOpenAngle); if (progress < 0) progress = 0; if (progress > 1) progress = 1; return progress; } // 获得当前点击位置所成角度与开始角度之间的数值差 private float getDiffAngle(float px, float py) { float angle = getAngle(px, py); float diffAngle; diffAngle = angle - mRotateAngle; if (diffAngle < 0) { diffAngle = (diffAngle + CIRCLE_ANGLE) % CIRCLE_ANGLE; } diffAngle = diffAngle - mOpenAngle / 2; return diffAngle; } // 计算指定位置与内容区域中心点的夹角 private float getAngle(float px, float py) { float angle = (float) ((Math.atan2(py - mCenterY, px - mCenterX)) * 180 / 3.14f); if (angle < 0) { angle += 360; } return angle; } // 计算指定位置与上次位置的距离 private float getDistance(float px, float py) { return (float) Math.sqrt((px - mThumbX) * (px - mThumbX) + (py - mThumbY) * (py - mThumbY)); } // 计算坐标圆点的距离 private double getDistanceWithCenter(float px, float py) { return Math.sqrt((px - mCenterX) * (px - mCenterX) + (py - mCenterY) * (py - mCenterY)); } private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics()); } /** * 获取相同距离下,圆弧对应的第三点坐标 * * @param mPoint 圆弧坐标 * 第三点距离第二点的距离 * DEFAULT_DISTANCE_BETWEEN_TEXTPOINT_AND_ARC 距离 */ private Point calcOutsidePoint(Point mPoint) { double newX = (mPoint.x * (mArcProgressBarRadius + mTextDefaultDistance) - mCenterX * mTextDefaultDistance) / mArcProgressBarRadius; double newY = (mPoint.y * (mArcProgressBarRadius + mTextDefaultDistance) - mCenterY * mTextDefaultDistance) / mArcProgressBarRadius; return new Point((int) newX, (int) newY); } // 计算拖动块应该显示的位置 private void computeThumbPos(float present) { if (present < 0) present = 0; if (present > 1) present = 1; if (null == mSeekPathMeasure) return; float distance = mSeekPathMeasure.getLength() * present; mSeekPathMeasure.getPosTan(distance, mTempPos, mTempTan); mThumbX = mTempPos[0]; mThumbY = mTempPos[1]; Point mmTextPoint = calcNewPoint(new Point((int) mThumbX, (int) mThumbY), new Point((int) mCenterX, (int) mCenterY), mRotateAngle); mSweepAngle = getDiffAngle(mmTextPoint.x, mmTextPoint.y); if (mSweepAngle < 0) mSweepAngle = 0; // Log.i("mSweepAngle", "mSweepAngle:" + mSweepAngle); mTextPoint = calcOutsidePoint(mmTextPoint); } //--- 线性取色 --------------------------------------------------------------------------------- /** * 获取当前进度的具体颜色 * * @return 当前进度在渐变中的颜色 */ public int getColor() { return getColor(mProgressPresent); } /** * 获取某个百分比位置的颜色 * * @param radio 取值[0,1] * @return 最终颜色 */ private int getColor(float radio) { float diatance = 1.0f / (mArcColors.length - 1); int startColor; int endColor; if (radio >= 1) { return mArcColors[mArcColors.length - 1]; } for (int i = 0; i < mArcColors.length; i++) { if (radio <= i * diatance) { if (i == 0) { return mArcColors[0]; } startColor = mArcColors[i - 1]; endColor = mArcColors[i]; float areaRadio = getAreaRadio(radio, diatance * (i - 1), diatance * i); return getColorFrom(startColor, endColor, areaRadio); } } return -1; } /** * 计算当前比例在子区间的比例 * * @param radio 总比例 * @param startPosition 子区间开始位置 * @param endPosition 子区间结束位置 * @return 自区间比例[0, 1] */ private float getAreaRadio(float radio, float startPosition, float endPosition) { return (radio - startPosition) / (endPosition - startPosition); } /** * 取两个颜色间的渐变区间 中的某一点的颜色 * * @param startColor 开始的颜色 * @param endColor 结束的颜色 * @param radio 比例 [0, 1] * @return 选中点的颜色 */ private int getColorFrom(int startColor, int endColor, float radio) { int redStart = Color.red(startColor); int blueStart = Color.blue(startColor); int greenStart = Color.green(startColor); int redEnd = Color.red(endColor); int blueEnd = Color.blue(endColor); int greenEnd = Color.green(endColor); int red = (int) (redStart + ((redEnd - redStart) * radio + 0.5)); int greed = (int) (greenStart + ((greenEnd - greenStart) * radio + 0.5)); int blue = (int) (blueStart + ((blueEnd - blueStart) * radio + 0.5)); return Color.argb(255, red, greed, blue); } /** * 计算某点绕中心点旋转一个角度后的坐标 * * @param pCenter 中心坐标 */ private static Point calcNewPoint(Point p, Point pCenter, float angle) { // calc arc float l = (float) ((angle * Math.PI) / 180); //sin/cos value float cosv = (float) Math.cos(l); float sinv = (float) Math.sin(l); // calc new point float newX = (float) ((p.x - pCenter.x) * cosv - (p.y - pCenter.y) * sinv + pCenter.x); float newY = (float) ((p.x - pCenter.x) * sinv + (p.y - pCenter.y) * cosv + pCenter.y); return new Point((int) newX, (int) newY); } // /** // * 求第三点坐标 // * // * @param pCenter 中心坐标 // * @param distance 第三点距离第二点的距离 // */ // private static Point calcOutsidePoint(Point p, Point pCenter, double distance) { // //计算2点之间的距离 // double ll = Math.sqrt((p.x - pCenter.x) * (p.x - pCenter.x) + (p.y - pCenter.y) * (p.y - pCenter.y)); // // // calc new point // double newX = (p.x * (ll + distance) - pCenter.x * distance) / ll; // double newY = (p.y * (ll + distance) - pCenter.y * distance) / ll; // return new Point((int) newX, (int) newY); // } // //****************************对外接口**************************************************** /** * 设置控件是否可用点击 * @param isClickable */ public void setIsClickable(boolean isClickable) { this.isClickable = isClickable; } /** * 设置进度 * * @param progress 进度值 */ public void setProgress(int progress) { System.out.println("setProgress = " + progress); if (progress > mMaxValue) progress = mMaxValue; if (progress < mMinValue) progress = mMinValue; mProgressPresent = (progress - mMinValue) * 1.0f / (mMaxValue - mMinValue); // System.out.println("setProgress present = " + mProgressPresent); // if (null != mOnProgressChangeListener) { // mOnProgressChangeListener.onProgressChanged(this, progress, false); // } computeThumbPos(mProgressPresent); postInvalidate(); } /** * 获取当前进度数值 * * @return 当前进度数值 */ public int getProgress() { return (int) (mProgressPresent * (mMaxValue - mMinValue)) + mMinValue; } /** * 设置颜色 * 3个颜色 渐变效果 * @param colors 颜色 */ public void setProgressBarColors(int[] colors) { if(colors.length < 2) return; mArcProgressBarColors = colors; resetmArcProgressBarColor(); postInvalidate(); } /** * 设置颜色 单种颜色 * 2019-8-15 * @param colors 颜色 */ public void setProgressBarColor(int colors) { int[] colorsArray = new int[]{colors, colors}; mArcProgressBarColors = colorsArray; resetmArcProgressBarColor(); postInvalidate(); } /** * 设置颜色 2个颜色以上 * 2019-8-15 * @param colors 颜色 */ public void setArcColors(int[] colors) { if(colors.length < 2) return; mArcColors = colors; resetShaderColor(); postInvalidate(); } /** * 设置颜色 * 单种颜色 * @param colors 颜色 */ public void setArcColor(int colors) { int[] colorsArray = new int[]{colors, colors, colors}; mArcColors = colorsArray; resetShaderColor(); postInvalidate(); } /** * 设置最大数值 * * @param max 最大数值 */ public void setMaxValue(int max) { mMaxValue = max; } /** * 设置最小数值 * * @param min 最小数值 */ public void setMinValue(int min) { mMinValue = min; } // /** // * 设置进度条颜色 // * // * @param arcProgressPainColor 进度条颜色 // */ // public void setProgressBarColor(int arcProgressPainColor) { // mArcProgressBarColor = arcProgressPainColor; // mArcProgressBarPaint.setColor(mArcProgressBarColor); // } /** * 设置进度显示值单位 * * @param progressBarUnitSring 进度显示值单位 */ public void setProgressBarUnitSring(String progressBarUnitSring) { mProgressBarUnitSring = progressBarUnitSring; } // /** // * 设置颜色 // * // * @param colorArrayRes 颜色资源 R.array.arc_color // */ // public void setArcColors(int colorArrayRes) { // setArcColors(getColorsByArrayResId(getContext(), colorArrayRes)); // } /** * 设置圆弧宽度,同时同步拖到按钮宽度 * @param arcWidth */ public void setArcWidthDefaultStyle(int arcWidth) { mArcWidth = dp2px(arcWidth); mThumbRadius = mArcWidth / 3; mThumbWidth = mArcWidth * 2 / 9; mArcPaint.setStrokeWidth(mArcWidth); mArcProgressBarPaint.setStrokeWidth(mArcWidth - 1); mThumbPaint.setStrokeWidth(mThumbWidth); } /** * 开口角度 * @param openAngle */ public void setOpenAngle(float openAngle) { mOpenAngle = openAngle; } /** * * @param thumbMode */ public void setThumbMode(int thumbMode) { mThumbMode = thumbMode; if (mThumbMode == THUMB_MODE_FILL) { mThumbPaint.setStyle(Paint.Style.FILL_AND_STROKE); } else if (mThumbMode == THUMB_MODE_FILL_STROKE) { mThumbPaint.setStyle(Paint.Style.FILL_AND_STROKE); } else { mThumbMode = THUMB_MODE_STROKE; mThumbPaint.setStyle(Paint.Style.STROKE); } } /** * * @param arcWidth */ public void setArcWidth(int arcWidth) { mArcWidth = dp2px(arcWidth); mArcPaint.setStrokeWidth(mArcWidth); mArcProgressBarPaint.setStrokeWidth(mArcWidth - 1); } /** * 拖动滑块半径 * @param thumbRadius */ public void setThumbRadius(int thumbRadius) { mThumbRadius = dp2px(thumbRadius); mThumbWidth = mThumbRadius * 2 / 3; mThumbPaint.setStrokeWidth(mThumbWidth); // initThumbPaint(); } /** * 拖动滑块颜色 * @param thumbColor */ public void setThumbColor(int thumbColor) { mThumbColor = thumbColor; mThumbPaint.setColor(mThumbColor); } /** * 进度文字大小 * @param textSize */ public void setProgressTextSize(int textSize){ mTextPaint.setTextSize(getTextSizeDip(textSize)); } /** * 进度文字颜色 * @param textPaintColor */ public void setProgressTextColor(int textPaintColor){ mTextPaint.setColor(textPaintColor); } /** * 进度文字与圆弧距离 * @param textDefaultDistance */ public void setTextDefaultDistance(int textDefaultDistance) { mTextDefaultDistance = dp2px(textDefaultDistance); } // endregion ----------------------------------------------------------------------------------- // region 状态回调 ------------------------------------------------------------------------------ private OnProgressChangeListener mOnProgressChangeListener; public void setOnProgressChangeListener(OnProgressChangeListener onProgressChangeListener) { mOnProgressChangeListener = onProgressChangeListener; } public interface OnProgressChangeListener { /** * 进度发生变化 * * @param seekBar 拖动条 * @param progress 当前进度数值 * @param isUser 是否是用户操作, true 表示用户拖动, false 表示通过代码设置 */ void onProgressChanged(HDLArcSeekBar seekBar, int progress, boolean isUser); /** * 用户开始拖动 * * @param seekBar 拖动条 */ void onStartTrackingTouch(HDLArcSeekBar seekBar); /** * 用户结束拖动 * * @param seekBar 拖动条 */ void onStopTrackingTouch(HDLArcSeekBar seekBar); } // endregion ----------------------------------------------------------------------------------- }