|
@@ -0,0 +1,357 @@
|
|
|
+package com.wdkl.ncs.android.lib.widget
|
|
|
+
|
|
|
+import android.animation.Animator
|
|
|
+import android.animation.AnimatorListenerAdapter
|
|
|
+import android.animation.ObjectAnimator
|
|
|
+import android.content.Context
|
|
|
+import android.graphics.*
|
|
|
+import android.os.Looper
|
|
|
+import android.text.TextPaint
|
|
|
+import android.util.AttributeSet
|
|
|
+import android.util.TypedValue
|
|
|
+import android.view.View
|
|
|
+import com.wdkl.ncs.android.lib.R
|
|
|
+
|
|
|
+class ProgressView :View{
|
|
|
+
|
|
|
+ private var mPaint: Paint? = null
|
|
|
+ private var mTextPaint: TextPaint? = null
|
|
|
+
|
|
|
+ private var mRadius: Int
|
|
|
+ private var mBorderColor: Int
|
|
|
+ private var mProgressColor: Int = 0
|
|
|
+ private var mProgressDescColor: Int
|
|
|
+ private var mMax: Int = 0
|
|
|
+ private var mProgress: Int = 0
|
|
|
+ private var mBorderWidth: Int
|
|
|
+ private var mIsShowDesc: Boolean = false
|
|
|
+ private var mHaveChangeColor: Boolean = false
|
|
|
+
|
|
|
+ private val DEFAULT_MAX = 10
|
|
|
+ private val DEFAULT_PROGRESS = 0
|
|
|
+ var changeColor = arrayOf("#CDFDCB", "#65FDCC", "#65FDCC", "#55CA7C", "#55CA7C", "#52CD53", "#52CD53", "#52CD53", "#3D9A5C", "#3D9A5C")
|
|
|
+
|
|
|
+ private val DEFAULT_RADIUS = TypedValue.applyDimension(
|
|
|
+ TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt()
|
|
|
+ private val DEFAULT_BORDER_COLOR = Color.parseColor("#FD0005")
|
|
|
+ private val DEFAULT_PROGRESS_COLOR = Color.parseColor("#77D178")
|
|
|
+ private val DEFAULT_PROGRESS_DESC_COLOR = Color.parseColor("#000000")
|
|
|
+ private val DEFAULT_BORDER_WIDTH = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, resources.displayMetrics).toInt()
|
|
|
+ private val DEFAULT_ISSHOWDESC = true
|
|
|
+
|
|
|
+ private var mWidth: Int = 0
|
|
|
+ private var mHeight: Int = 0
|
|
|
+ private var mTextBounds: Rect? = null
|
|
|
+ private var mProgressDesc: String? = ""
|
|
|
+
|
|
|
+ private var mOnFinishedListener: OnFinishedListener? = null
|
|
|
+ private var mOnAnimationEndListener: OnAnimationEndListener? = null
|
|
|
+
|
|
|
+ constructor(context: Context): this(context, null)
|
|
|
+
|
|
|
+ constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0)
|
|
|
+
|
|
|
+ constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr) {
|
|
|
+ val a = context.obtainStyledAttributes(attrs, R.styleable.ProgressView)
|
|
|
+ mMax = a.getInt(R.styleable.ProgressView_max, DEFAULT_MAX)
|
|
|
+ mProgress = a.getInt(R.styleable.ProgressView_progress, DEFAULT_PROGRESS)
|
|
|
+ mRadius = a.getDimension(R.styleable.ProgressView_progressRadius, DEFAULT_RADIUS.toFloat()).toInt()
|
|
|
+ mBorderColor = a.getColor(R.styleable.ProgressView_borderColor, DEFAULT_BORDER_COLOR)
|
|
|
+ mProgressColor = a.getColor(R.styleable.ProgressView_progressColor, DEFAULT_PROGRESS_COLOR)
|
|
|
+ mProgressDescColor = a.getColor(R.styleable.ProgressView_progressDescColor, DEFAULT_PROGRESS_DESC_COLOR)
|
|
|
+ mBorderWidth = a.getDimension(R.styleable.ProgressView_borderWidth, DEFAULT_BORDER_WIDTH.toFloat()).toInt()
|
|
|
+ mProgressDesc = a.getString(R.styleable.ProgressView_progressDesc)
|
|
|
+ mIsShowDesc = a.getBoolean(R.styleable.ProgressView_isShowDesc, DEFAULT_ISSHOWDESC)
|
|
|
+ mHaveChangeColor = a.getBoolean(R.styleable.ProgressView_haveChangeColor, true)
|
|
|
+ a.recycle()
|
|
|
+ init()
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun init() {
|
|
|
+ mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
|
|
+ mTextBounds = Rect()
|
|
|
+ mTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
|
|
|
+ mTextPaint!!.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, resources.displayMetrics)
|
|
|
+ mTextPaint!!.color = mProgressDescColor
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置结束监听
|
|
|
+ *
|
|
|
+ * @param onFinishedListener
|
|
|
+ */
|
|
|
+ fun setOnFinishedListener(onFinishedListener: OnFinishedListener) {
|
|
|
+ mOnFinishedListener = onFinishedListener
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置进度停止监听
|
|
|
+ *
|
|
|
+ * @param onAnimationEndListener
|
|
|
+ */
|
|
|
+ fun setOnAnimationEndListener(onAnimationEndListener: OnAnimationEndListener) {
|
|
|
+ mOnAnimationEndListener = onAnimationEndListener
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
|
+ super.onSizeChanged(w, h, oldw, oldh)
|
|
|
+ mWidth = w
|
|
|
+ mHeight = h
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onDraw(canvas: Canvas) {
|
|
|
+ drawBorder(canvas)
|
|
|
+ drawProgress(canvas)
|
|
|
+ if (mIsShowDesc) drawProgressDesc(canvas)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun drawBorder(canvas: Canvas) {
|
|
|
+ mPaint!!.reset()
|
|
|
+ mPaint!!.style = Paint.Style.STROKE
|
|
|
+ mPaint!!.strokeJoin = Paint.Join.MITER
|
|
|
+ mPaint!!.isAntiAlias = true
|
|
|
+ mPaint!!.color = mBorderColor
|
|
|
+ mPaint!!.strokeWidth = mBorderWidth.toFloat()
|
|
|
+
|
|
|
+ val left = mBorderWidth / 2
|
|
|
+ val top = mBorderWidth / 2
|
|
|
+ val right = mWidth - mBorderWidth / 2
|
|
|
+ val bottom = mHeight - mBorderWidth / 2
|
|
|
+
|
|
|
+ val path = Path()
|
|
|
+ path.moveTo((left + mRadius).toFloat(), top.toFloat())
|
|
|
+ path.lineTo((right - mRadius).toFloat(), top.toFloat())
|
|
|
+ path.arcTo(RectF((right - 2 * mRadius).toFloat(), top.toFloat(), right.toFloat(), (top + 2 * mRadius).toFloat()), -90f, 90f)
|
|
|
+ path.lineTo(right.toFloat(), (bottom - mRadius).toFloat())
|
|
|
+ path.arcTo(RectF((right - 2 * mRadius).toFloat(), (bottom - 2 * mRadius).toFloat(), right.toFloat(), bottom.toFloat()), 0f, 90f)
|
|
|
+ path.lineTo((left + mRadius).toFloat(), bottom.toFloat())
|
|
|
+ path.arcTo(RectF(left.toFloat(), (bottom - 2 * mRadius).toFloat(), (left + 2 * mRadius).toFloat(), bottom.toFloat()), 90f, 90f)
|
|
|
+ path.lineTo(left.toFloat(), (top + mRadius).toFloat())
|
|
|
+ path.arcTo(RectF(left.toFloat(), top.toFloat(), (left + 2 * mRadius).toFloat(), (top + 2 * mRadius).toFloat()), 180f, 90f)
|
|
|
+ path.close()
|
|
|
+ canvas.drawPath(path, mPaint!!)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun drawProgress(canvas: Canvas) {
|
|
|
+
|
|
|
+ mPaint!!.reset()
|
|
|
+ mPaint!!.style = Paint.Style.FILL
|
|
|
+ mPaint!!.isAntiAlias = true
|
|
|
+ mPaint!!.color = mProgressColor
|
|
|
+ mPaint!!.strokeWidth = mBorderWidth.toFloat()
|
|
|
+
|
|
|
+ val left = mBorderWidth * .5f
|
|
|
+ val top = mBorderWidth * .5f
|
|
|
+ val right = mWidth - mBorderWidth * .5f
|
|
|
+ val bottom = mHeight - mBorderWidth * .5f
|
|
|
+
|
|
|
+ val path = Path()
|
|
|
+ path.moveTo(left, top + mRadius)
|
|
|
+ val scale = mProgress * 1f / mMax / (mRadius * 1f / (right - left))
|
|
|
+ val scale2 = mProgress * 1f / mMax / ((right - mRadius) * 1f / (right - left))
|
|
|
+ if (scale <= 1) {
|
|
|
+ val a = scale * mRadius
|
|
|
+ val angle = Math.acos(((mRadius - a) / mRadius).toDouble())
|
|
|
+ path.arcTo(RectF(left, top, left + 2 * mRadius, top + 2 * mRadius), 180f, (angle * 180 / Math.PI).toFloat())
|
|
|
+ val y = (Math.pow(Math.pow(mRadius.toDouble(), 2.0) - Math.pow((a - mRadius).toDouble(), 2.0), 0.5) + bottom - mRadius).toFloat()
|
|
|
+ path.lineTo(left + a, y)
|
|
|
+ path.arcTo(RectF(left, bottom - 2 * mRadius, left + 2 * mRadius, bottom), 180 - (angle * 180 / Math.PI).toFloat(),
|
|
|
+ (angle * 180 / Math.PI).toFloat())
|
|
|
+ path.close()
|
|
|
+ canvas.drawPath(path, mPaint!!)
|
|
|
+ } else if (scale2 <= 1) {
|
|
|
+ path.arcTo(RectF(left, top, left + 2 * mRadius, top + 2 * mRadius), 180f, 90f)
|
|
|
+ path.lineTo(left + mProgress * 1f / mMax * (right - left), top)
|
|
|
+ path.lineTo(left + mProgress * 1f / mMax * (right - left), bottom)
|
|
|
+ path.lineTo(left + mRadius, bottom)
|
|
|
+ path.arcTo(RectF(left, bottom - 2 * mRadius, left + 2 * mRadius, bottom), 90f, 90f)
|
|
|
+ path.close()
|
|
|
+ canvas.drawPath(path, mPaint!!)
|
|
|
+ } else {
|
|
|
+ val a = mProgress * 1f / mMax * (right - left) - (right - mRadius)
|
|
|
+ val angle = Math.asin((a / mRadius).toDouble())
|
|
|
+ path.arcTo(RectF(left, top, left + 2 * mRadius, top + 2 * mRadius), 180f, 90f)
|
|
|
+ path.lineTo(right - mRadius, top)
|
|
|
+ path.arcTo(RectF(right - 2 * mRadius, top, right, top + 2 * mRadius), -90f, (angle * 180 / Math.PI).toFloat())
|
|
|
+ val y = Math.pow(Math.pow(mRadius.toDouble(), 2.0) - Math.pow(a.toDouble(), 2.0), .5) + top.toDouble() + mRadius.toDouble()
|
|
|
+
|
|
|
+ path.lineTo(right - mRadius + a, y.toFloat())
|
|
|
+ path.arcTo(RectF(right - 2 * mRadius, bottom - 2 * mRadius,
|
|
|
+ right, bottom), (90 - angle * 180 / Math.PI).toFloat(),
|
|
|
+ (angle * 180 / Math.PI).toFloat())
|
|
|
+ path.lineTo(left + mRadius, bottom)
|
|
|
+ path.arcTo(RectF(left, bottom - 2 * mRadius,
|
|
|
+ left + 2 * mRadius, bottom), 90f, 90f)
|
|
|
+ path.close()
|
|
|
+
|
|
|
+ canvas.drawPath(path, mPaint!!)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun drawProgressDesc(canvas: Canvas) {
|
|
|
+ var finalProgressDesc = ""
|
|
|
+ if (100 == mMax) {
|
|
|
+ finalProgressDesc = "$mProgressDesc$mProgress %"
|
|
|
+ } else {
|
|
|
+ finalProgressDesc = "$mProgressDesc$mProgress/$mMax"
|
|
|
+ }
|
|
|
+ mTextPaint!!.getTextBounds(finalProgressDesc, 0, finalProgressDesc.length, mTextBounds)
|
|
|
+ canvas.drawText(finalProgressDesc, (mWidth / 2.0 - mTextBounds!!.width() / 2.0).toInt().toFloat(), (mHeight / 2.0 - (mTextPaint!!.ascent() + mTextPaint!!.descent()) / 2.0f).toInt().toFloat(), mTextPaint!!)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private fun setProgress(progress: Int) {
|
|
|
+
|
|
|
+ mProgress = if (progress > mMax) mMax else progress
|
|
|
+ invalidateView()
|
|
|
+
|
|
|
+ if (mProgress >= mMax && mOnFinishedListener != null) {
|
|
|
+ mOnFinishedListener!!.onFinish()
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 得到ProgressBar的最大进度
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ fun getMax(): Int {
|
|
|
+ return mMax
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取当前ProgressBar的进度
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ fun getProgress(): Int {
|
|
|
+ return mProgress
|
|
|
+ }
|
|
|
+
|
|
|
+ fun setMaxProgress(max: Int) {
|
|
|
+ this.mMax = if (max < 0) 0 else max
|
|
|
+ invalidateView()
|
|
|
+ }
|
|
|
+
|
|
|
+ fun setProgressDesc(desc: String) {
|
|
|
+ mProgressDesc = desc
|
|
|
+ invalidateView()
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置当前进度条的进度(默认动画时间1.5s)
|
|
|
+ *
|
|
|
+ * @param progress
|
|
|
+ */
|
|
|
+ fun setCurProgress(progress: Int) {
|
|
|
+ if (mHaveChangeColor) {
|
|
|
+ setProgressColor(getProgressChangeColor(progress.toFloat() / mMax.toFloat()))
|
|
|
+ } else {
|
|
|
+ setProgressColor(DEFAULT_PROGRESS_COLOR)
|
|
|
+ }
|
|
|
+ val animator = ObjectAnimator.ofInt(this, "progress", progress).setDuration(0)
|
|
|
+ animator.addListener(object : AnimatorListenerAdapter() {
|
|
|
+ override fun onAnimationEnd(animation: Animator) {
|
|
|
+ super.onAnimationEnd(animation)
|
|
|
+ if (mOnAnimationEndListener != null) {
|
|
|
+ mOnAnimationEndListener!!.onAnimationEnd()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ animator.start()
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置当前进度条的进度
|
|
|
+ *
|
|
|
+ * @param progress 目标进度
|
|
|
+ * @param duration 动画时长
|
|
|
+ */
|
|
|
+ fun setCurProgress(progress: Int, duration: Long) {
|
|
|
+ if (mHaveChangeColor) {
|
|
|
+ setProgressColor(getProgressChangeColor(progress.toFloat() / mMax.toFloat()))
|
|
|
+ } else {
|
|
|
+ setProgressColor(DEFAULT_PROGRESS_COLOR)
|
|
|
+ }
|
|
|
+ val animator = ObjectAnimator.ofInt(this, "progress", progress).setDuration(duration)
|
|
|
+ animator.addListener(object : AnimatorListenerAdapter() {
|
|
|
+ override fun onAnimationEnd(animation: Animator) {
|
|
|
+ super.onAnimationEnd(animation)
|
|
|
+ if (mOnAnimationEndListener != null) {
|
|
|
+ mOnAnimationEndListener!!.onAnimationEnd()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ animator.start()
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun getProgressChangeColor(percentage: Float): Int {
|
|
|
+ if (0 < percentage && percentage <= 0.1) {
|
|
|
+ return Color.parseColor(changeColor[0])
|
|
|
+ } else if (0.1 < percentage && percentage <= 0.2) {
|
|
|
+ return Color.parseColor(changeColor[1])
|
|
|
+ } else if (0.2 < percentage && percentage <= 0.3) {
|
|
|
+ return Color.parseColor(changeColor[2])
|
|
|
+ } else if (0.3 < percentage && percentage <= 0.4) {
|
|
|
+ return Color.parseColor(changeColor[3])
|
|
|
+ } else if (0.4 < percentage && percentage <= 0.5) {
|
|
|
+ return Color.parseColor(changeColor[4])
|
|
|
+ } else if (0.5 < percentage && percentage <= 0.6) {
|
|
|
+ return Color.parseColor(changeColor[5])
|
|
|
+ } else if (0.6 < percentage && percentage <= 0.7) {
|
|
|
+ return Color.parseColor(changeColor[6])
|
|
|
+ } else if (0.7 < percentage && percentage <= 0.8) {
|
|
|
+ return Color.parseColor(changeColor[7])
|
|
|
+ } else if (0.8 < percentage && percentage <= 0.9) {
|
|
|
+ return Color.parseColor(changeColor[8])
|
|
|
+ } else if (0.9 < percentage && percentage <= 1.0) {
|
|
|
+ return Color.parseColor(changeColor[9])
|
|
|
+ }
|
|
|
+ return DEFAULT_PROGRESS_COLOR
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置ProgressBar的颜色
|
|
|
+ *
|
|
|
+ * @param color
|
|
|
+ */
|
|
|
+ fun setProgressColor(color: Int) {
|
|
|
+ mProgressColor = color
|
|
|
+ invalidateView()
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置是否显示当前进度
|
|
|
+ *
|
|
|
+ * @param isShowDesc true:显示
|
|
|
+ */
|
|
|
+ fun setIsShowDesc(isShowDesc: Boolean) {
|
|
|
+ mIsShowDesc = isShowDesc
|
|
|
+ invalidateView()
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置是否需要颜色渐变
|
|
|
+ */
|
|
|
+ fun setHaveChangeColor(haveChangeColor: Boolean) {
|
|
|
+ mHaveChangeColor = haveChangeColor
|
|
|
+ invalidateView()
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun invalidateView() {
|
|
|
+ if (Looper.getMainLooper() == Looper.myLooper()) {
|
|
|
+ invalidate()
|
|
|
+ } else {
|
|
|
+ postInvalidate()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ interface OnFinishedListener {
|
|
|
+ fun onFinish()
|
|
|
+ }
|
|
|
+
|
|
|
+ interface OnAnimationEndListener {
|
|
|
+ fun onAnimationEnd()
|
|
|
+ }
|
|
|
+}
|