Skip to content

Commit

Permalink
Merge pull request #2 from Saketme/ms/clickable-spans
Browse files Browse the repository at this point in the history
Move from URL span handling to clickable span, addresses issue #1
  • Loading branch information
saket authored Oct 17, 2016
2 parents 8acb489 + 188ec20 commit b7f8b79
Showing 1 changed file with 56 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.BackgroundColorSpan;
import android.text.style.ClickableSpan;
Expand Down Expand Up @@ -186,25 +187,25 @@ public boolean onTouchEvent(TextView view, Spannable text, MotionEvent event) {
view.setAutoLinkMask(0);
}

final URLSpan touchedURLSpan = findURLSpanUnderTouch(view, text, event);
final ClickableSpanWithText touchedClickableSpan = findClickableSpanUnderTouch(view, text, event);

// Toggle highlight
if (touchedURLSpan != null) {
highlightURL(view, touchedURLSpan, text);
if (touchedClickableSpan != null) {
highlightUrl(view, touchedClickableSpan, text);
} else {
removeUrlHighlightColor(view);
}

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchStartedOverLink = touchedURLSpan != null;
touchStartedOverLink = touchedClickableSpan != null;
return true;

case MotionEvent.ACTION_UP:
// Register a click only if the touch started on an URL. That is, the touch did not start
// elsewhere and ended up on an URL.
if (touchedURLSpan != null && touchStartedOverLink) {
dispatchUrlClick(view, touchedURLSpan);
if (touchedClickableSpan != null && touchStartedOverLink) {
dispatchUrlClick(view, touchedClickableSpan);
removeUrlHighlightColor(view);

}
Expand All @@ -224,11 +225,11 @@ public boolean onTouchEvent(TextView view, Spannable text, MotionEvent event) {
}

/**
* Determines the touched location inside the TextView's text and returns the URLSpan found under it (if any).
* Determines the touched location inside the TextView's text and returns the ClickableSpan found under it (if any).
*
* @return The touched URLSpan or null.
* @return The touched ClickableSpan or null.
*/
protected URLSpan findURLSpanUnderTouch(TextView textView, Spannable text, MotionEvent event) {
protected ClickableSpanWithText findClickableSpanUnderTouch(TextView textView, Spannable text, MotionEvent event) {
// So we need to find the location in text where touch was made, regardless of whether the TextView
// has scrollable text. That is, not the entire text is currently visible.
int touchX = (int) event.getX();
Expand All @@ -252,14 +253,14 @@ protected URLSpan findURLSpanUnderTouch(TextView textView, Spannable text, Motio
touchedLineBounds.bottom = layout.getLineBottom(touchedLine);

if (touchedLineBounds.contains(touchX, touchY)) {
// Find any URLSpan that lies under the touched area
// Find any ClickableSpan that lies under the touched area
final Object[] spans = text.getSpans(touchOffset, touchOffset, SPAN_CLASS);
for (final Object span : spans) {
if (span instanceof URLSpan) {
return ((URLSpan) span);
if (span instanceof ClickableSpan) {
return ClickableSpanWithText.ofSpan(textView, (ClickableSpan) span);
}
}
// No URLSpan found under the touched location.
// No ClickableSpan found under the touched location.
return null;

} else {
Expand All @@ -271,14 +272,14 @@ protected URLSpan findURLSpanUnderTouch(TextView textView, Spannable text, Motio
/**
* Adds a highlight background color span to the TextView.
*/
protected void highlightURL(TextView textView, URLSpan urlSpan, Spannable text) {
protected void highlightUrl(TextView textView, ClickableSpanWithText spanWithText, Spannable text) {
if (isUrlHighlighted) {
return;
}
isUrlHighlighted = true;

final int spanStart = text.getSpanStart(urlSpan);
final int spanEnd = text.getSpanEnd(urlSpan);
final int spanStart = text.getSpanStart(spanWithText.span());
final int spanEnd = text.getSpanEnd(spanWithText.span());
text.setSpan(new BackgroundColorSpan(textView.getHighlightColor()), spanStart, spanEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(text);

Expand Down Expand Up @@ -306,13 +307,49 @@ protected void removeUrlHighlightColor(TextView textView) {
Selection.removeSelection(text);
}

protected void dispatchUrlClick(TextView textView, URLSpan span) {
final String spanUrl = span.getURL();
protected void dispatchUrlClick(TextView textView, ClickableSpanWithText spanWithText) {
final String spanUrl = spanWithText.text();
boolean handled = onLinkClickListener != null && onLinkClickListener.onClick(textView, spanUrl);
if (!handled) {
// Let Android handle this click.
span.onClick(textView);
spanWithText.span().onClick(textView);
}
}

/**
* A wrapper with a clickable span and its text.
*/
static class ClickableSpanWithText {

private ClickableSpan span;
private String text;

static ClickableSpanWithText ofSpan(TextView textView, ClickableSpan span) {
Spanned s = (Spanned) textView.getText();
String text;
if (span instanceof URLSpan) {
text = ((URLSpan) span).getURL();
} else {
int start = s.getSpanStart(span);
int end = s.getSpanEnd(span);
text = s.subSequence(start, end).toString();
}
return new ClickableSpanWithText(span, text);
}

private ClickableSpanWithText(ClickableSpan span, String text) {
this.span = span;
this.text = text;
}

ClickableSpan span() {
return span;
}

String text() {
return text;
}

}

}

0 comments on commit b7f8b79

Please sign in to comment.