@matiks/rn-stroke-text 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,9 +14,7 @@ import android.view.View
14
14
  import kotlin.math.ceil
15
15
  import kotlin.math.max
16
16
 
17
- class StrokeTextView(
18
- context: Context
19
- ) : View(context) {
17
+ class StrokeTextView(context: Context) : View(context) {
20
18
 
21
19
  // ─────────────────────────────────────────────
22
20
  // Props
@@ -37,15 +35,14 @@ class StrokeTextView(
37
35
  // Paints
38
36
  // ─────────────────────────────────────────────
39
37
 
40
- private val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
41
- style = Paint.Style.FILL
42
- }
38
+ private val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
43
39
 
44
- private val strokePaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
45
- style = Paint.Style.STROKE
46
- strokeJoin = Paint.Join.ROUND
47
- strokeCap = Paint.Cap.ROUND
48
- }
40
+ private val strokePaint =
41
+ TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
42
+ style = Paint.Style.STROKE
43
+ strokeJoin = Paint.Join.ROUND
44
+ strokeCap = Paint.Cap.ROUND
45
+ }
49
46
 
50
47
  // ─────────────────────────────────────────────
51
48
  // Layout
@@ -54,6 +51,7 @@ class StrokeTextView(
54
51
  private var textLayout: StaticLayout? = null
55
52
  private var strokeLayout: StaticLayout? = null
56
53
  private var layoutDirty = true
54
+ private var lastLayoutWidth: Int = -1 // Track width used for layout
57
55
 
58
56
  private val fontCache = HashMap<String, Typeface?>()
59
57
 
@@ -69,10 +67,7 @@ class StrokeTextView(
69
67
 
70
68
  val desiredHeight = (textLayout?.height ?: 0).coerceAtLeast(1)
71
69
 
72
- setMeasuredDimension(
73
- measuredWidth,
74
- resolveSize(desiredHeight, heightMeasureSpec)
75
- )
70
+ setMeasuredDimension(measuredWidth, resolveSize(desiredHeight, heightMeasureSpec))
76
71
  }
77
72
 
78
73
  private fun resolveMeasuredWidth(spec: Int): Int {
@@ -120,76 +115,100 @@ class StrokeTextView(
120
115
  }
121
116
 
122
117
  private fun ensureLayout(width: Int) {
123
- if (!layoutDirty && textLayout != null) return
124
-
125
118
  val safeWidth = width.coerceAtLeast(1)
119
+ if (!layoutDirty && textLayout != null && lastLayoutWidth == safeWidth) return
126
120
  updatePaints()
127
121
 
128
122
  var displayText: CharSequence =
129
- if (ellipsis) {
130
- TextUtils.ellipsize(
131
- text,
132
- textPaint,
133
- safeWidth.toFloat(),
134
- TextUtils.TruncateAt.END
135
- )
136
- } else {
137
- text
138
- }
123
+ if (ellipsis) {
124
+ TextUtils.ellipsize(
125
+ text,
126
+ textPaint,
127
+ safeWidth.toFloat(),
128
+ TextUtils.TruncateAt.END
129
+ )
130
+ } else {
131
+ text
132
+ }
139
133
 
140
134
  // Use StaticLayout.Builder for API 23+
141
- val builder = StaticLayout.Builder.obtain(displayText, 0, displayText.length, textPaint, safeWidth)
142
- .setAlignment(alignment)
143
- .setLineSpacing(0f, 1f)
144
- .setIncludePad(false)
135
+ val builder =
136
+ StaticLayout.Builder.obtain(
137
+ displayText,
138
+ 0,
139
+ displayText.length,
140
+ textPaint,
141
+ safeWidth
142
+ )
143
+ .setAlignment(alignment)
144
+ .setLineSpacing(0f, 1f)
145
+ .setIncludePad(false)
145
146
 
146
147
  var layout = builder.build()
147
148
 
148
149
  if (numberOfLines > 0 && layout.lineCount > numberOfLines) {
149
150
  val end = layout.getLineEnd(numberOfLines - 1)
150
151
  displayText = displayText.subSequence(0, end)
151
-
152
- val truncatedBuilder = StaticLayout.Builder.obtain(displayText, 0, displayText.length, textPaint, safeWidth)
153
- .setAlignment(alignment)
154
- .setLineSpacing(0f, 1f)
155
- .setIncludePad(false)
156
-
152
+
153
+ val truncatedBuilder =
154
+ StaticLayout.Builder.obtain(
155
+ displayText,
156
+ 0,
157
+ displayText.length,
158
+ textPaint,
159
+ safeWidth
160
+ )
161
+ .setAlignment(alignment)
162
+ .setLineSpacing(0f, 1f)
163
+ .setIncludePad(false)
164
+
157
165
  layout = truncatedBuilder.build()
158
166
  }
159
167
 
160
168
  textLayout = layout
161
-
169
+
162
170
  // For stroke layout, we use the same text
163
- val strokeBuilder = StaticLayout.Builder.obtain(displayText, 0, displayText.length, strokePaint, safeWidth)
164
- .setAlignment(alignment)
165
- .setLineSpacing(0f, 1f)
166
- .setIncludePad(false)
167
-
171
+ val strokeBuilder =
172
+ StaticLayout.Builder.obtain(
173
+ displayText,
174
+ 0,
175
+ displayText.length,
176
+ strokePaint,
177
+ safeWidth
178
+ )
179
+ .setAlignment(alignment)
180
+ .setLineSpacing(0f, 1f)
181
+ .setIncludePad(false)
182
+
168
183
  strokeLayout = strokeBuilder.build()
169
184
 
185
+ lastLayoutWidth = safeWidth // Remember the width used
170
186
  layoutDirty = false
171
187
  }
172
188
 
173
189
  fun getTextDimensions(): Pair<Double, Double> {
174
190
  updatePaints()
175
-
176
- // Ensure we have a valid layout for measurement.
191
+
192
+ // Ensure we have a valid layout for measurement.
177
193
  // If width is 0/unspecified, we measure intrinsic width.
178
- val measureWidth = if (customWidthPx > 0f) {
179
- ceil(customWidthPx).toInt()
180
- } else {
181
- ceil(measureTextWidth()).toInt()
182
- }
183
-
194
+ val measureWidth =
195
+ if (customWidthPx > 0f) {
196
+ ceil(customWidthPx).toInt()
197
+ } else {
198
+ ceil(measureTextWidth()).toInt()
199
+ }
200
+
184
201
  // We create a temporary layout if needed to get accurate height for the given width
185
- // But since ensureLayout caches the layout based on last width, we might want to just ensure layout exists.
186
- // For accurate "intrinsic" measurement, we usually want the width to be the text width if not constrained.
187
-
202
+ // But since ensureLayout caches the layout based on last width, we might want to just
203
+ // ensure layout exists.
204
+ // For accurate "intrinsic" measurement, we usually want the width to be the text width if
205
+ // not constrained.
206
+
188
207
  ensureLayout(measureWidth)
189
208
 
190
209
  val wPx = textLayout?.width?.toFloat() ?: 0f
191
210
  val hPx = textLayout?.height?.toFloat() ?: 0f
192
-
211
+
193
212
  return Pair(pxToDp(wPx).toDouble(), pxToDp(hPx).toDouble())
194
213
  }
195
214
 
@@ -224,7 +243,7 @@ class StrokeTextView(
224
243
  }
225
244
 
226
245
  fun setFontSize(value: Float) {
227
- val px = sp(value)
246
+ val px = dp(value)
228
247
  if (fontSizePx != px) {
229
248
  fontSizePx = px
230
249
  invalidateLayout()
@@ -263,12 +282,13 @@ class StrokeTextView(
263
282
  }
264
283
 
265
284
  fun setTextAlignment(value: String) {
266
- val newAlignment = when (value) {
267
- "left" -> Layout.Alignment.ALIGN_NORMAL
268
- "right" -> Layout.Alignment.ALIGN_OPPOSITE
269
- "center" -> Layout.Alignment.ALIGN_CENTER
270
- else -> alignment
271
- }
285
+ val newAlignment =
286
+ when (value) {
287
+ "left" -> Layout.Alignment.ALIGN_NORMAL
288
+ "right" -> Layout.Alignment.ALIGN_OPPOSITE
289
+ "center" -> Layout.Alignment.ALIGN_CENTER
290
+ else -> alignment
291
+ }
272
292
  if (alignment != newAlignment) {
273
293
  alignment = newAlignment
274
294
  invalidateLayout()
@@ -308,35 +328,27 @@ class StrokeTextView(
308
328
  }
309
329
 
310
330
  private fun sp(v: Float): Float =
311
- TypedValue.applyDimension(
312
- TypedValue.COMPLEX_UNIT_SP,
313
- v,
314
- resources.displayMetrics
315
- )
331
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, v, resources.displayMetrics)
316
332
 
317
333
  private fun dp(v: Float): Float =
318
- TypedValue.applyDimension(
319
- TypedValue.COMPLEX_UNIT_DIP,
320
- v,
321
- resources.displayMetrics
322
- )
334
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, v, resources.displayMetrics)
323
335
 
324
- private fun pxToDp(px: Float): Float =
325
- px / resources.displayMetrics.density
336
+ private fun pxToDp(px: Float): Float = px / resources.displayMetrics.density
326
337
 
327
338
  private fun parseColor(color: String): Int =
328
- try {
329
- Color.parseColor(color)
330
- } catch (_: Exception) {
331
- Color.BLACK
332
- }
339
+ try {
340
+ Color.parseColor(color)
341
+ } catch (_: Exception) {
342
+ Color.BLACK
343
+ }
333
344
 
334
345
  private fun getFont(fontFamily: String): Typeface? {
335
- return fontCache[fontFamily] ?: run {
336
- val tf = FontUtil.getFont(context, fontFamily)
337
- fontCache[fontFamily] = tf
338
- tf
339
- }
346
+ return fontCache[fontFamily]
347
+ ?: run {
348
+ val tf = FontUtil.getFont(context, fontFamily)
349
+ fontCache[fontFamily] = tf
350
+ tf
351
+ }
340
352
  }
341
353
 
342
354
  init {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matiks/rn-stroke-text",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "rn-stroke-text",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",