class BusLineVue: ConstraintLayout {
constructor(context: Context): super(context)
constructor(context: Context, attrs: AttributeSet): super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr)
private val busIV = ImageView(context)
private val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG)
private val linePaint = Paint(Paint.ANTI_ALIAS_FLAG)
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG)
private val textRect = Rect(0, 0, 0, 0)
private val path = Path()
private val pathMeasure get() = PathMeasure(path, false)
private var busStopData: List<Triple<Pair<Path, String>, Pair<PointF, RectF>, Paint>> = listOf()
private val margin = 50f
private var progress = 0f
init {
setWillNotDraw(false)
busIV.setImageResource(androidx.appcompat.R.drawable.btn_checkbox_checked_mtrl)
val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
layoutParams.width = 80
layoutParams.height = 80
busIV.layoutParams = layoutParams
busIV.visibility = View.INVISIBLE
addView(busIV)
bgPaint.color = Color.parseColor("#000000")
bgPaint.style = Paint.Style.STROKE
bgPaint.strokeWidth = 4f
linePaint.color = Color.parseColor("#1DDB16")
linePaint.style = Paint.Style.STROKE
linePaint.strokeWidth = 4f
textPaint.color = Color.parseColor("#000000")
textPaint.textSize = 40f
post {
val mWidth = measuredWidth.toFloat()
val mHeight = measuredHeight.toFloat()
// create path
path.reset()
path.moveTo(mWidth / 2f, margin)
path.arcTo(margin, margin, mHeight - margin, mHeight - margin, 270f, -180f, false)
path.arcTo(mWidth - mHeight - margin, margin, mWidth - margin, mHeight - margin, 90f, -180f, false)
path.close()
// create bus stop data
busStopData = floatArrayOf(0f, 0.25f, 0.5f, 0.75f).mapIndexed { index, value ->
val points = FloatArray(2)
pathMeasure.getPosTan(pathMeasure.length * value, points, null)
val pointF = PointF(points[0], points[1])
val path = Path()
val rectF = RectF(pointF.x - 20f, pointF.y - 20f, pointF.x + 20f, pointF.y + 20f)
path.addRect(rectF, Path.Direction.CCW)
val text = when(index) {
0 -> "정류장 1"
1 -> "정류장 2"
2 -> "정류장 3"
else -> "정류장 4"
}
val stopPaint = Paint(Paint.ANTI_ALIAS_FLAG)
stopPaint.color = Color.parseColor("#000000")
stopPaint.style = Paint.Style.FILL
Triple(Pair(path, text), Pair(pointF, rectF), stopPaint)
}
invalidate()
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// draw background
canvas.drawPath(path, bgPaint)
// draw line
canvas.drawPath(path, linePaint)
// draw stop
busStopData.forEachIndexed { index, data ->
canvas.drawPath(data.first.first, data.third)
// draw stop name
textPaint.getTextBounds(data.first.second, 0, data.first.second.length, textRect)
when(index) {
0 -> canvas.drawText(data.first.second, data.second.first.x - textRect.width() / 2, data.second.first.y + textRect.height() * 2f, textPaint)
1 -> canvas.drawText(data.first.second, data.second.first.x + margin, data.second.first.y + textRect.height() * 0.5f, textPaint)
2 -> canvas.drawText(data.first.second, data.second.first.x - textRect.width() / 2, data.second.first.y - textRect.height() * 1.5f, textPaint)
else -> canvas.drawText(data.first.second, data.second.first.x - (textRect.width() + margin), data.second.first.y + textRect.height() * 0.5f, textPaint)
}
}
}
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
// draw bus
val pos = FloatArray(2)
val tan = FloatArray(2)
pathMeasure.getPosTan(pathMeasure.length * progress, pos, tan)
val pointF = PointF(pos[0], pos[1])
val angle = atan2(tan[1].toDouble(), tan[0].toDouble())
canvas.translate(pointF.x - 40, pointF.y - 40)
canvas.rotate(Math.toDegrees(angle).toFloat() - 90, 40f, 40f)
busIV.draw(canvas)
if (busStopData.isNotEmpty()) {
val zeroStop = RectF(busStopData[0].second.second.left, busStopData[0].second.second.top, busStopData[0].second.second.right - 20f, busStopData[0].second.second.bottom)
if (zeroStop.contains(pointF)) { busStopData.forEachIndexed { index, _ -> busStopData[index].third.color = Color.parseColor("#000000") } }
if (busStopData[1].second.second.contains(pointF)) { busStopData[1].third.color = Color.parseColor("#FF0000") }
if (busStopData[2].second.second.contains(pointF)) { busStopData[2].third.color = Color.parseColor("#FF0000") }
if (busStopData[3].second.second.contains(pointF)) { busStopData[3].third.color = Color.parseColor("#FF0000") }
}
}
fun set(progress: Float) {
val totalDist = pathMeasure.length
linePaint.pathEffect = DashPathEffect(floatArrayOf(totalDist, totalDist), totalDist * (1f - progress))
this.progress = progress
invalidate()
}
}