class DashBoardView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : View(context, attrs) {
private var radius = 0f set(value) { field = value outArcRadius = value - outArcMargin }
var maxPointer = 120 set(value) { field = value everyPointerRotate = ((360f - keepAngle) / value) } var warningPointer = 110
var keepAngle = 90f set(value) { field = value maxAngle = 360f - value everyPointerRotate = ((360f - value) / maxPointer) } private var maxAngle = 360f - keepAngle
private var everyPointerRotate = ((360f - keepAngle) / maxPointer)
var rotateAngel = 90 + keepAngle / 2f
private var currentPointer = 0 set(value) { field = when { value > maxPointer -> { maxPointer } value < 0 -> { 0 } else -> { value } } invalidate() }
private val frame1To4 = Runnable { currentPointer += everyFrameProgress } private val frameLast = Runnable { currentPointer = needProgress } var animationDuration = 50L set(value) { field = value onFrameTime = value / 3 onFrameTimeHardwareAccelerated = value / 5 }
private var onFrameTime = animationDuration / 3 private var needProgress = 0 private var everyFrameProgress = 0 private val frameHandler: Handler by lazy { Handler() }
fun setCurrentPointerAnimator(value: Int) { val real = when { value > maxPointer -> { maxPointer } value < 0 -> { 0 } else -> { value } } if (real != currentPointer) { needProgress = real everyFrameProgress = (real - currentPointer) / 3 frameHandler.removeCallbacksAndMessages(null) currentPointer += everyFrameProgress frameHandler.postDelayed(frame1To4, onFrameTime) frameHandler.postDelayed(frameLast, onFrameTime * 2) } }
private var onFrameTimeHardwareAccelerated = animationDuration / 5
fun setCurrentPointerAnimatorHardwareAccelerated(value: Int) { val real = when { value > maxPointer -> { maxPointer } value < 0 -> { 0 } else -> { value } } if (real != currentPointer) { needProgress = real everyFrameProgress = (real - currentPointer) / 5 frameHandler.removeCallbacksAndMessages(null) currentPointer += everyFrameProgress frameHandler.postDelayed(frame1To4, onFrameTime) frameHandler.postDelayed(frame1To4, onFrameTime * 2) frameHandler.postDelayed(frame1To4, onFrameTime * 3) frameHandler.postDelayed(frameLast, onFrameTime * 4) } }
private val normalSimplePointerColor: Int by lazy { Color.parseColor("#555555") } private val waringFocusPointerColor: Int by lazy { resources.getColor(R.color.colorAlarm, null) } private val waringSimplePointerColor: Int by lazy { resources.getColor(R.color.colorAlarmSimple, null) }
private val longPointerWidth = 16f private val longPointerHeight = 4f
private val normalPointerWidth = 7f private val normalPointerHeight = 2f
private val pointerMargin = 4f private val pointerRadius: Float by lazy { radius - pointerMargin }
private var pointerTextSize = 24f private var pointerTextMargin = 8
private val pointerPaint: Paint by lazy { val pointerPaint = Paint() pointerPaint.isAntiAlias = true pointerPaint.textSize = pointerTextSize pointerPaint.textAlign = Paint.Align.CENTER pointerPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC) pointerPaint }
private val fontMetrics: Paint.FontMetrics by lazy { pointerPaint.fontMetrics }
var isDrawSimplePointer = true
var isMediumPointerUseFocusColor = true
var isPointerTextSimple = false
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val widthMode = MeasureSpec.getMode(widthMeasureSpec) val widthSize = MeasureSpec.getSize(widthMeasureSpec) val heightMode = MeasureSpec.getMode(heightMeasureSpec) val heightSize = MeasureSpec.getSize(heightMeasureSpec)
var width: Int var height: Int if (widthMode == MeasureSpec.EXACTLY) { width = widthSize } else { width = paddingLeft + 128 * 2 + paddingRight if (widthMode == MeasureSpec.AT_MOST) { width = min(width, widthSize) } }
if (heightMode == MeasureSpec.EXACTLY) { height = heightSize } else { height = paddingTop + 128 * 2 + paddingBottom if (heightMode == MeasureSpec.AT_MOST) { height = min(height, heightSize) } }
setMeasuredDimension(width, height)
radius = min(measuredWidth - paddingLeft - paddingRight, measuredHeight - paddingTop - paddingBottom) / 2f }
override fun onDraw(canvas: Canvas) { canvas.translate(paddingLeft + radius, paddingTop + radius) canvas.rotate(rotateAngel) canvas.save() drawDefaultPointerLine(canvas) canvas.restore() if (currentPointer in 1..maxPointer) { val currentAngle = maxAngle * currentPointer / maxPointer if (currentAngle > 0) { drawPointerLine(canvas) drawArc(canvas, currentAngle) drawShadow(canvas, currentAngle) } } }
private val textRect = Rect()
private fun drawDefaultPointerLine(canvas: Canvas) { for (i in 0..maxPointer) { if (i < warningPointer) { if (isMediumPointerUseFocusColor) { if (i % 5 == 0) { pointerPaint.color = Color.WHITE } else { pointerPaint.color = normalSimplePointerColor } } else { if (i % 10 == 0) { pointerPaint.color = Color.WHITE } else { pointerPaint.color = normalSimplePointerColor } } } else { if (isMediumPointerUseFocusColor) { if (i % 5 == 0) { pointerPaint.color = waringFocusPointerColor } else { pointerPaint.color = waringSimplePointerColor } } else { if (i % 10 == 0) { pointerPaint.color = waringFocusPointerColor } else { pointerPaint.color = waringSimplePointerColor } } }
if (i % 10 == 0) { pointerPaint.strokeWidth = longPointerHeight canvas.drawLine(pointerRadius, 0f, (pointerRadius - longPointerWidth), 0f, pointerPaint)
val text = if (isPointerTextSimple) { (i / 10).toString() } else { i.toString() }
canvas.save() pointerPaint.getTextBounds(text, 0, text.length, textRect) val currentCenterX = (pointerRadius - longPointerWidth - pointerTextMargin - max(textRect.height(), textRect.width()) / 2f) pointerPaint.measureText(text) canvas.translate(currentCenterX, 0f) canvas.rotate(360 - rotateAngel - everyPointerRotate * i)
val textBaseLine = (fontMetrics.bottom - fontMetrics.top) / 2f - fontMetrics.bottom canvas.drawText(text, 0f, textBaseLine, pointerPaint) canvas.restore() } else { if (isDrawSimplePointer || i % 5 == 0) { pointerPaint.strokeWidth = normalPointerHeight canvas.drawLine(pointerRadius, 0f, (pointerRadius - normalPointerWidth), 0f, pointerPaint) } } canvas.rotate(everyPointerRotate) } }
var outArcWidth = 5f var outArcMargin = 4 set(value) { field = value outArcRadius = radius - outArcMargin } private var outArcRadius = radius - outArcMargin set(value) { field = value outArcRect.set(-value, -value, value, value) } private val outArcRect: RectF by lazy { RectF(-outArcRadius, -outArcRadius, outArcRadius, outArcRadius) }
private val normalArcColor: Int by lazy { resources.getColor(R.color.colorNormal, null) }
private val arcPaint: Paint by lazy { val arcPaint = Paint() arcPaint.isAntiAlias = true arcPaint.style = Paint.Style.STROKE arcPaint }
var inArcWidth = -1f var inArcRadius = -1f set(value) { field = value inArcRect.set(-value, -value, value, value) } private val inArcRect: RectF by lazy { RectF(-inArcRadius, -inArcRadius, inArcRadius, inArcRadius) }
private val inArcGradientAngle: Int by lazy { (maxAngle * 8 / maxPointer).toInt() }
var pointerLength = 52f var pointerWidth = 3f private val pointerPath = Path()
private fun drawPointerLine(canvas: Canvas) { if (currentPointer < warningPointer) { pointerPaint.color = normalArcColor } else { pointerPaint.color = waringFocusPointerColor } canvas.save() for (i in 0..currentPointer) { if (i % 10 == 0) { pointerPaint.strokeWidth = longPointerHeight canvas.drawLine(pointerRadius, 0f, (pointerRadius - longPointerWidth), 0f, pointerPaint) } else { if (isDrawSimplePointer || i % 5 == 0) { pointerPaint.strokeWidth = normalPointerHeight canvas.drawLine(pointerRadius, 0f, (pointerRadius - normalPointerWidth), 0f, pointerPaint) } } if (i == currentPointer) { pointerPath.reset() pointerPath.moveTo(outArcRadius, longPointerHeight / 2) if (inArcWidth != -1f) { pointerPath.lineTo(inArcRadius - inArcWidth + 5, 0f) } else { pointerPath.lineTo(outArcRadius - pointerLength, 0f) } pointerPath.lineTo(outArcRadius, -pointerWidth) pointerPath.close() canvas.drawPath(pointerPath, pointerPaint) } canvas.rotate(everyPointerRotate) } canvas.restore() }
private fun drawArc(canvas: Canvas, currentAngle: Float) { val paintColor = if (currentPointer < warningPointer) { normalArcColor } else { waringFocusPointerColor }
arcPaint.strokeWidth = outArcWidth arcPaint.color = paintColor canvas.drawArc(outArcRect, -0.6f, currentAngle + 1f, false, arcPaint)
arcPaint.strokeWidth = inArcWidth if (inArcRadius != -1f) { if (currentAngle < inArcGradientAngle) { val startAngle = 0f val end = currentAngle.toInt() for (i in 0..end) { arcPaint.color = getGradient((i / end.toFloat()), Color.TRANSPARENT, paintColor) if (i >= end - 1) { canvas.drawArc(inArcRect, startAngle + i, 1f, false, arcPaint) } else { canvas.drawArc(inArcRect, startAngle + i, 2f, false, arcPaint) } } } else { val startAngle = 0f for (i in 0..inArcGradientAngle) { arcPaint.color = getGradient((i / inArcGradientAngle.toFloat()), Color.TRANSPARENT, paintColor) canvas.drawArc(inArcRect, startAngle + i, 2f, false, arcPaint) } arcPaint.color = paintColor canvas.drawArc(inArcRect, inArcGradientAngle.toFloat(), currentAngle - inArcGradientAngle, false, arcPaint) } } }
private fun getGradient(fraction: Float, startColor: Int, endColor: Int): Int { var newFraction = fraction if (newFraction > 1) newFraction = 1f val alphaStart = Color.alpha(startColor) val redStart = Color.red(startColor) val blueStart = Color.blue(startColor) val greenStart = Color.green(startColor) val alphaEnd = Color.alpha(endColor) val redEnd = Color.red(endColor) val blueEnd = Color.blue(endColor) val greenEnd = Color.green(endColor) val alphaDifference = alphaEnd - alphaStart val redDifference = redEnd - redStart val blueDifference = blueEnd - blueStart val greenDifference = greenEnd - greenStart val alphaCurrent = (alphaStart + newFraction * alphaDifference).toInt() val redCurrent = (redStart + newFraction * redDifference).toInt() val blueCurrent = (blueStart + newFraction * blueDifference).toInt() val greenCurrent = (greenStart + newFraction * greenDifference).toInt() return Color.argb(alphaCurrent, redCurrent, greenCurrent, blueCurrent) }
var shadowWidth = 60 private val shadowRect = RectF()
private fun drawShadow(canvas: Canvas, currentAngle: Float) { val showAttenuation: Float val paintColor = if (currentPointer < warningPointer) { showAttenuation = 0.35f normalArcColor } else { showAttenuation = 0.45f waringFocusPointerColor } val shadowsRadius = outArcRadius - outArcWidth + 4
arcPaint.strokeWidth = 2f
for (i in shadowWidth downTo 0) { arcPaint.color = getGradient(i / shadowWidth.toFloat() * showAttenuation, Color.TRANSPARENT, paintColor) val rectRadius = shadowsRadius - shadowWidth + i shadowRect.set(-rectRadius, -rectRadius, rectRadius, rectRadius) canvas.drawArc(shadowRect, -1f, currentAngle, false, arcPaint) } } }
|