I've run into a problem with TextView
. I can make it selectable using setTextIsSelectable(true)
, but when I enable links to be clicked via setMovementMethod(LinkMovementMethod.getInstance())
, it is no longer selectable.
Please note, I don't mean making raw links clickable, but rather making actual words clickable by loading the TextView
with HTML markup using something like setText(Html.fromHtml("<a href='http://stackoverflow.com'>Hello World!</a>"))
.
Just like Buttons and ImageViews we can add onClickListeners to TextViews by simply adding the attribute android:onClick="myMethod" to your TextView XML tag. The other way, TextView tv = (TextView) this.
A TextView displays text to the user and optionally allows them to edit it. A TextView is a complete text editor, however the basic class is configured to not allow editing.
TextView is the user interface which displays the text message on the screen to the user. It is based on the layout size, style, and color, etc. TextView is used to set and display the text according to our specifications.
oakes's answer cause exception on double tap on textview
java.lang.IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0...
I looked at the onTouchEvent impletentation in LinkMovementMethod and found that it removes selection when textview doesn't contain link. In this case selection starts from empty value and application crash when user try to change it.
... if (link.length != 0) { if (action == MotionEvent.ACTION_UP) { link[0].onClick(widget); } else if (action == MotionEvent.ACTION_DOWN) { Selection.setSelection(buffer, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0])); } return true; } else { Selection.removeSelection(buffer); } ...
So i override onTouchEvent method, and it works fine.
public class CustomMovementMethod extends LinkMovementMethod { @Override public boolean canSelectArbitrarily () { return true; } @Override public void initialize(TextView widget, Spannable text) { Selection.setSelection(text, text.length()); } @Override public void onTakeFocus(TextView view, Spannable text, int dir) { if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) { if (view.getLayout() == null) { // This shouldn't be null, but do something sensible if it is. Selection.setSelection(text, text.length()); } } else { Selection.setSelection(text, text.length()); } } @Override public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { int action = event.getAction(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { int x = (int) event.getX(); int y = (int) event.getY(); x -= widget.getTotalPaddingLeft(); y -= widget.getTotalPaddingTop(); x += widget.getScrollX(); y += widget.getScrollY(); Layout layout = widget.getLayout(); int line = layout.getLineForVertical(y); int off = layout.getOffsetForHorizontal(line, x); ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); if (link.length != 0) { if (action == MotionEvent.ACTION_UP) { link[0].onClick(widget); } else if (action == MotionEvent.ACTION_DOWN) { Selection.setSelection(buffer, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0])); } return true; } } return Touch.onTouchEvent(widget, buffer, event); } }
Hope it will be helpful for someone.
I figured it out. You need to subclass LinkMovementMethod and add support for text selection. It's really unfortunate that it doesn't support it natively. I just overrode the relevant methods using the equivalent ones from the source code for ArrowKeyMovementMethod
. I guess that's one benefit of Android being open source!
public class CustomMovementMethod extends LinkMovementMethod { @Override public boolean canSelectArbitrarily () { return true; } @Override public void initialize(TextView widget, Spannable text) { Selection.setSelection(text, text.length()); } @Override public void onTakeFocus(TextView view, Spannable text, int dir) { if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) { if (view.getLayout() == null) { // This shouldn't be null, but do something sensible if it is. Selection.setSelection(text, text.length()); } } else { Selection.setSelection(text, text.length()); } } }
To use it, just instantiate it directly, like so:
textView.setMovementMethod(new CustomMovementMethod());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With