javaandroidandroid-edittextclickablespan

ClickableSpan in the EditText to the end of text calls click() to the end of line


I have an EditText and the TextWatcher, in which I check for patterns to determine where to create ClickableSpans.

private void checkSpans(){
    Matcher matcher = Pattern.compile("(^|[ \\t\\n])#\\w+").matcher(text);
    while (matcher.find()) {
        BlueClickableSpan[] spans = getText().getSpans(matcher.start(), matcher.end(), BlueClickableSpan.class);
        for (BlueClickableSpan span : spans) {
            text.removeSpan(span);
        }
        text.setSpan(new BlueClickableSpan(),
                matcher.start(),
                matcher.end(),
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
}

Here is my custom Span class.

private class BlueClickableSpan extends ClickableSpan {

        @Override
        public void onClick(View view) {
            Spanned s = getText();
            int start = s.getSpanStart(this);
            int end = s.getSpanEnd(this);
            String clickedString = s.subSequence(start, end).toString().trim();
            if (onSpanClick != null)
                onSpanClick.onClick(clickedString);
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            ds.setColor(ContextCompat.getColor(IdeaFlowApp.getAppContext(), R.color.main_blue));
        }
    }

The problem is, when I end my text with a span and there is no characters after it and I tap AFTER it, it still counts as a click to span and onClick is called. Because of that I cant place cursor right after the span to continue editing. But if I print any symbol, even whitespace after the span, I can easily place the cursor after the span and everything works fine. Is there any way to fix the clicks in edittext after the clickable span if there are no symbols beyond?


Solution

  • I found a solution to my problem in dor506s answer here. But there were some problems with multi-line text with spans, so I made some tweaks:

    public class MovementMethod extends 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 = getLayout();
                int line = layout.getLineForVertical(y);
                int off = layout.getOffsetForHorizontal(line, x);
    
                int lineLength = line;
                String[] lines = getText().toString().split("\n");
                for (int i = 0; i <= line; i++) {
                    lineLength += lines[i].length();
                }
    
                if (off >= lineLength) {
                   // Return true so click won't be triggered in the leftover empty space
                    return true;
                }
            }
    
            return super.onTouchEvent(widget, buffer, event);
        }
    }
    

    Maybe not the most beautiful multi-line solution, but it worked for me.