-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDirectionalTextView.kt
More file actions
140 lines (121 loc) · 4.39 KB
/
DirectionalTextView.kt
File metadata and controls
140 lines (121 loc) · 4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package com.muhammetkonukcu.examples.views
import android.content.Context
import android.graphics.Canvas
import android.graphics.Path
import android.graphics.Rect
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import com.muhammetkonukcu.examples.R
/***
* @author MuhammetKonukcu
* createdAt 19.04.2025
*/
class DirectionalTextView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : AppCompatTextView(context, attrs, defStyle) {
companion object {
const val ORIENTATION_UP_TO_DOWN = 0
const val ORIENTATION_DOWN_TO_UP = 1
const val ORIENTATION_LEFT_TO_RIGHT = 2
const val ORIENTATION_RIGHT_TO_LEFT = 3
}
private val textBoundsRect = Rect()
private var orientation = ORIENTATION_UP_TO_DOWN
init {
attrs?.let {
context.obtainStyledAttributes(it, R.styleable.DirectionalTextView).apply {
orientation = getInt(
R.styleable.DirectionalTextView_direction,
ORIENTATION_UP_TO_DOWN
)
recycle()
}
}
requestLayout()
invalidate()
}
fun setOrientation(newOrientation: Int) {
orientation = newOrientation
requestLayout()
invalidate()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
paint.getTextBounds(text.toString(), 0, text.length, textBoundsRect)
val isHorizontal = orientation == ORIENTATION_LEFT_TO_RIGHT ||
orientation == ORIENTATION_RIGHT_TO_LEFT
val measuredW = if (isHorizontal)
calculateHeightSpec(widthMeasureSpec)
else
calculateWidthSpec(widthMeasureSpec)
val measuredH = if (isHorizontal)
calculateWidthSpec(heightMeasureSpec)
else
calculateHeightSpec(heightMeasureSpec)
setMeasuredDimension(measuredW, measuredH)
}
private fun calculateWidthSpec(measureSpec: Int): Int {
val mode = MeasureSpec.getMode(measureSpec)
val size = MeasureSpec.getSize(measureSpec)
return when (mode) {
MeasureSpec.EXACTLY -> size
MeasureSpec.AT_MOST -> {
val content = textBoundsRect.height() + paddingTop + paddingBottom
content.coerceAtMost(size)
}
else -> textBoundsRect.height() + paddingTop + paddingBottom
}
}
private fun calculateHeightSpec(measureSpec: Int): Int {
val mode = MeasureSpec.getMode(measureSpec)
val size = MeasureSpec.getSize(measureSpec)
return when (mode) {
MeasureSpec.EXACTLY -> size
MeasureSpec.AT_MOST -> {
val content = textBoundsRect.width() + paddingLeft + paddingRight
content.coerceAtMost(size)
}
else -> textBoundsRect.width() + paddingLeft + paddingRight
}
}
override fun onDraw(canvas: Canvas) {
canvas.save()
val path = buildTextPath()
paint.color = currentTextColor
canvas.drawTextOnPath(text.toString(), path, 0f, 0f, paint)
canvas.restore()
}
private fun buildTextPath(): Path {
val w = width
val h = height
val tW = textBoundsRect.width()
val tH = textBoundsRect.height()
val path = Path()
val (startX, startY, endX, endY) = when (orientation) {
ORIENTATION_UP_TO_DOWN -> {
val x = (w - tH) / 2
val y = (h - tW) / 2
listOf(x, y, x, (h + tW) / 2)
}
ORIENTATION_DOWN_TO_UP -> {
val x = (w + tH) / 2
val y = (h + tW) / 2
listOf(x, y, x, (h - tW) / 2)
}
ORIENTATION_LEFT_TO_RIGHT -> {
val y = (h + tH) / 2
val x = (w - tW) / 2
listOf(x, y, (w + tW) / 2, y)
}
else /* RIGHT_TO_LEFT */ -> {
val y = (h - tH) / 2
val x = (w + tW) / 2
listOf(x, y, (w - tW) / 2, y)
}
}
path.moveTo(startX.toFloat(), startY.toFloat())
path.lineTo(endX.toFloat(), endY.toFloat())
return path
}
}