Background
before you say it's a repost, i have to say i have tried reading other posts, so please keep reading.
The basic way to show links inside a textView can be done in the next way:
final Spanned text = Html.fromHtml(getString(R.string.test));
textView.setText(text);
textView.setMovementMethod(LinkMovementMethod.getInstance());
and the string.xml file could contain :
<string name="test" formatted="false">
<![CDATA[
This<br />
is<br />
<<a href="http://www.google.com">a</a><br />
test
]]>
</string>
However, we might want to capture the event of clicking on the link, and handle it ourself. not only that, but the link might not need to be of a real url.
many posts exists to show how to do it, but none that i've tried handle it well.
some let the intent being handled on the app, which is quite cumbersome and might mean other apps could handle it too (right?) and i think it requires that the link would be to a specific pattern.
some let the LinkMovementMethod handle it but ruin the clickability effect of the links. some say to change the color to something, but that's not the same.
The problem
How do you simply add a listener to the textview that will tell you which text area was clicked inside it?
What I've tried
currently, i've used LinkMovementMethod . the issues of my code are:
- because i don't know how to mimic the clicking as on normal links, it looks weird when you click on it. the background doesn't look right, and i think i need to use the default one of the device, but can't find out how. as oppsosed to what some may say, it's not by using the next code:
textview.getLinkTextColors().getDefaultColor()
- on some cases the text might stay clicked .
here's the code:
MainActivity.java (sample of how to use)
public class MainActivity extends Activity {
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final String a = "<a href='/a'>aaaa</a>123456<a href='/b'>bbbb</a>7890";
final TextView textView = (TextView) findViewById(R.id.span);
textView.setText(Html.fromHtml(a));
final LinkClickMovementMethodExt linkMovementMethod = new LinkClickMovementMethodExt();
linkMovementMethod.setOnLinkClickedListener(new OnLinkClickedListener() {
@Override
public void onLinkClicked(final TextView clickedTextView, final URLSpan clickedSpan) {
Toast.makeText(MainActivity.this, clickedSpan.getURL(), Toast.LENGTH_SHORT).show();
}
});
textView.setMovementMethod(linkMovementMethod);
}
}
LinkClickMovementMethodExt.java
/** a class to handle clicking on links of textViews . based on http://stackoverflow.com/a/16182500/878126 */
public class LinkClickMovementMethodExt extends LinkMovementMethod {
// TODO check how to get the default background of a clicked link
private final BackgroundColorSpan LINK_COLOR = new BackgroundColorSpan(0xFFACE0F4);
private final Class<URLSpan> spanClass = URLSpan.class;
private OnLinkClickedListener mOnLinkClickedListener;
public interface OnLinkClickedListener {
public void onLinkClicked(TextView textView, URLSpan clickedSpan);
}
@Override
public boolean onTouchEvent(final TextView textView, final Spannable buffer, final MotionEvent event) {
final int action = event.getAction();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= textView.getTotalPaddingLeft();
y -= textView.getTotalPaddingTop();
x += textView.getScrollX();
y += textView.getScrollY();
final Layout layout = textView.getLayout();
final int line = layout.getLineForVertical(y);
final int off = layout.getOffsetForHorizontal(line, x);
/**
* get you interest span
*/
final Object[] spans = buffer.getSpans(off, off, spanClass);
if (spans.length != 0) {
if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer, buffer.getSpanStart(spans[0]), buffer.getSpanEnd(spans[0]));
for (final Object span : spans) {
if (span instanceof URLSpan) {
final int start = Selection.getSelectionStart(textView.getText());
final int end = Selection.getSelectionEnd(textView.getText());
final Spannable selectedSpan = (Spannable) textView.getText();
selectedSpan.setSpan(LINK_COLOR, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(selectedSpan);
if (mOnLinkClickedListener != null)
mOnLinkClickedListener.onLinkClicked(textView, (URLSpan) span);
}
}
return false;
} else if (action == MotionEvent.ACTION_UP) {
final Spannable span = (Spannable) textView.getText();
span.removeSpan(LINK_COLOR);
textView.setText(span);
return false;
}
}
}
return super.onTouchEvent(textView, buffer, event);
}
@Override
public boolean canSelectArbitrarily() {
return true;
}
@Override
public boolean onKeyUp(final TextView widget, final Spannable buffer, final int keyCode, final KeyEvent event) {
return false;
}
public void setOnLinkClickedListener(final OnLinkClickedListener onLinkClickedListener) {
this.mOnLinkClickedListener = onLinkClickedListener;
}
}