@@ -162,7 +162,7 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
162
162
val x = event.x.toInt()
163
163
val y = event.y.toInt()
164
164
165
- val clickableSpan = getClickableSpanInCoords (x, y)
165
+ val clickableSpan = getSpanInCoords (x, y, ClickableSpan :: class .java )
166
166
167
167
if (clickableSpan == null ) {
168
168
clearSelection()
@@ -182,33 +182,53 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
182
182
return true
183
183
}
184
184
185
- /* *
186
- * Get the clickable span that is at the exact coordinates
187
- *
188
- * @param x x-position of the click
189
- * @param y y-position of the click
190
- * @return a clickable span that's located where the click occurred, or: `null` if no clickable
191
- * span was located there
192
- */
193
- private fun getClickableSpanInCoords (x : Int , y : Int ): ClickableSpan ? {
185
+ private fun <T > getSpanInCoords (x : Int , y : Int , clazz : Class <T >): T ? {
194
186
val offset = getTextOffsetAt(x, y)
195
187
if (offset < 0 ) {
196
188
return null
197
189
}
198
190
199
191
val spanned = text as ? Spanned ? : return null
200
192
201
- val clickableSpans = spanned.getSpans(offset, offset, ClickableSpan ::class .java)
202
- if (clickableSpans.isNotEmpty()) {
203
- return clickableSpans[0 ]
193
+ val spans = spanned.getSpans(offset, offset, clazz)
194
+ if (spans.isEmpty()) {
195
+ return null
196
+ }
197
+
198
+ // When we have multiple spans marked with SPAN_EXCLUSIVE_INCLUSIVE next to each other, both
199
+ // spans are returned by getSpans
200
+ check(spans.size <= 2 )
201
+ for (span in spans) {
202
+ val spanFlags = spanned.getSpanFlags(span)
203
+ val inclusiveStart =
204
+ if ((spanFlags and Spanned .SPAN_INCLUSIVE_INCLUSIVE ) != 0 ||
205
+ (spanFlags and Spanned .SPAN_INCLUSIVE_EXCLUSIVE ) != 0 ) {
206
+ spanned.getSpanStart(span)
207
+ } else {
208
+ spanned.getSpanStart(span) + 1
209
+ }
210
+ val inclusiveEnd =
211
+ if ((spanFlags and Spanned .SPAN_INCLUSIVE_INCLUSIVE ) != 0 ||
212
+ (spanFlags and Spanned .SPAN_EXCLUSIVE_INCLUSIVE ) != 0 ) {
213
+ spanned.getSpanEnd(span)
214
+ } else {
215
+ spanned.getSpanEnd(span) - 1
216
+ }
217
+
218
+ if (offset >= inclusiveStart && offset <= inclusiveEnd) {
219
+ return span
220
+ }
204
221
}
205
222
206
223
return null
207
224
}
208
225
209
226
private fun getTextOffsetAt (x : Int , y : Int ): Int {
227
+ val layoutX = x - paddingLeft
228
+ val layoutY = y - (paddingTop + (preparedLayout?.verticalOffset?.roundToInt() ? : 0 ))
229
+
210
230
val layout = preparedLayout?.layout ? : return - 1
211
- val line = layout.getLineForVertical(y )
231
+ val line = layout.getLineForVertical(layoutY )
212
232
213
233
val left: Float
214
234
val right: Float
@@ -238,12 +258,12 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
238
258
right = if (rtl) layout.getParagraphRight(line).toFloat() else layout.getLineMax(line)
239
259
}
240
260
241
- if (x < left || x > right) {
261
+ if (layoutX < left || layoutX > right) {
242
262
return - 1
243
263
}
244
264
245
265
return try {
246
- layout.getOffsetForHorizontal(line, x .toFloat())
266
+ layout.getOffsetForHorizontal(line, layoutX .toFloat())
247
267
} catch (e: ArrayIndexOutOfBoundsException ) {
248
268
// This happens for bidi text on Android 7-8.
249
269
// See
@@ -332,20 +352,6 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
332
352
}
333
353
}
334
354
335
- override fun reactTagForTouch (touchX : Float , touchY : Float ): Int {
336
- val offset = getTextOffsetAt(touchX.roundToInt(), touchY.roundToInt())
337
- if (offset < 0 ) {
338
- return id
339
- }
340
-
341
- val spanned = text as ? Spanned ? : return id
342
- val reactSpans = spanned.getSpans(offset, offset, ReactTagSpan ::class .java)
343
- check(reactSpans.size <= 1 )
344
-
345
- return if (reactSpans.isNotEmpty()) {
346
- reactSpans[0 ].reactTag
347
- } else {
348
- id
349
- }
350
- }
355
+ override fun reactTagForTouch (touchX : Float , touchY : Float ): Int =
356
+ getSpanInCoords(x.roundToInt(), y.roundToInt(), ReactTagSpan ::class .java)?.reactTag ? : id
351
357
}
0 commit comments