3

Background

I wanted to use some URLSpan inside a textView, so I've used something like:

final Spanned spanned=Html.fromHtml(getString(R.string.test));
msgTextView.setText(spanned);

The problem

It works, but when clicking an item, it stays clicked. it's as if I still touch the link...

What I've tried

I've tried to clear the focus from the textView and to set the focus on another view, but none of those helped.

I've also tried to remove the UrlSpan and add a new one instead, each time it gets clicked, but it didn't work. Here's the code:

  public static Spannable setOnLinkClickedListener(final Spanned original,final IOnLinkClickListener listener)
    {
    final SpannableString result=new SpannableString(original);
    final URLSpan[] spans=result.getSpans(0,result.length(),URLSpan.class);
    for(final URLSpan span : spans)
      {
      final int start=result.getSpanStart(span);
      final int end=result.getSpanEnd(span);
      final int flags=result.getSpanFlags(span);
      result.removeSpan(span);
      final String url=span.getURL();
      result.setSpan(new CustomURLSpan(url,start,result,end,listener,flags),start,end,flags);
      }
    return result;
    }

  private static final class CustomURLSpan extends URLSpan
    {
    private final int                  _start;
    private final SpannableString      _result;
    private final String               _url;
    private final int                  _end;
    private final IOnLinkClickListener _listener;
    private final int                  _flags;

    private CustomURLSpan(final String url,final int start,final SpannableString result,final int end,final IOnLinkClickListener listener,final int flags)
      {
      super(url);
      _start=start;
      _result=result;
      _url=url;
      _end=end;
      _listener=listener;
      _flags=flags;
      }

    @Override
    public void onClick(final View widget)
      {
      if(_listener==null||!_listener.onClick(widget,_url))
        super.onClick(widget);
      _result.removeSpan(this);
      _result.setSpan(new CustomURLSpan(_url,_start,_result,_end,_listener,_flags),_start,_end,_flags);
      }
    }

The question

How do I clear the selection/clicking effect that stays on URLSpan each time it gets clicked?

android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • The brute-force approach is to replace the `URLSpan` with a fresh `URLSpan` when you want to revert to its original look. Otherwise, poke around `ClickableSpan` (the parent class of `URLSpan`), look at its `updateDrawState()` method, and try to make sense of where and when the link color is being changed. – CommonsWare Feb 16 '14 at 00:13
  • is it possible to replace just the current span instead of all of them? that's what i've tried and it didn't work too. i also don't understand why it has this behavior. – android developer Feb 16 '14 at 06:28
  • "is it possible to replace just the current span instead of all of them?" -- call `removeSpan()`, then `addSpan()`, passing in the same start/end values that the old span had. – CommonsWare Feb 16 '14 at 12:40
  • there is no "addSpan". there is "setSpan", and sadly even though i've tried to do it, it didn't work. i will now update my question to show my code, hoping you will find out what's wrong. – android developer Feb 16 '14 at 17:19

3 Answers3

1

Subclass LinkMovementMethod and override onTouchEvent to call Selection.removeSelection(buffer); after link[0].onClick(widget);.

// 1. Subclass LinkMovementMethod.
public class CustomLinkMovementMethod extends LinkMovementMethod {

    // 2. Copy this method from LinkMovementMethod.
    @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);
                    Selection.removeSelection(buffer); // 3. Add this line.
                } else if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer,
                            buffer.getSpanStart(link[0]),
                            buffer.getSpanEnd(link[0]));
                }

                return true;
            } else {
                Selection.removeSelection(buffer);
            }
        }

        return super.onTouchEvent(widget, buffer, event);
    }
}

BTW, if you use it in ListView, subclass TextView and override setPressed to call invalidate();, because ListView defers calling setPressed.

public class CustomTextView extends TextView {

    public CustomTextView(Context context) {
        super(context);
    }

    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void setPressed(boolean pressed) {
        super.setPressed(pressed);
        invalidate();
    }
}
hoshi
  • 1,677
  • 16
  • 22
0

I had the same issue before. Its probably because of android:textIsSelectable attribute of the textView you are using. Remove it or set it false and it will be solved.

0

this answer is work for me. I test in API19 API26 with Checkbox,CheckedTexView TextView all clarify

  • You mean "textView.setHighlightColor(Color.TRANSPARENT)" ? I meant how to fix the issue that it seems highlighted after clicking. Not to completely hide highlighting of text... – android developer Sep 16 '17 at 16:03