Search code examples
androidtagsimagespantokenautocompleteandroid-spannable

TextView with spans , how can I know which one is clicked on?


I am using this library, but from the onClick styles that they have, neither handles my needed case. https://github.com/splitwise/TokenAutoComplete

So I wanted to do my own. So I have a ContactsCompletionView, which is a TextView. and I override the onTouchEvent like this:

override fun onTouchEvent(event: MotionEvent): Boolean {
    val action = event.actionMasked
    val text = text
    var handled = super.onTouchEvent(event)
    if (isFocused && text != null && action == MotionEvent.ACTION_UP) {
        val offset = getOffsetForPosition(event.x, event.y)
        if (offset != -1) {
            var offseted = text.substring(offset, text.length)
            var indexLeft = offseted.indexOf("(") + 1
            var indexRight = offseted.indexOf(")")
            if (indexLeft > 0 && indexRight > indexLeft)
                Toast.makeText(context, offseted.substring(indexLeft, indexRight), Toast.LENGTH_SHORT).show()
        }
    }
    return handled
}

This is what they had, but I cannot use TokenImageSpan because it is a protected class:

@Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
    int action = event.getActionMasked();
    Editable text = getText();
    boolean handled = false;

    if (tokenClickStyle == TokenClickStyle.None) {
        handled = super.onTouchEvent(event);
    }

    if (isFocused() && text != null && lastLayout != null && action == MotionEvent.ACTION_UP) {

        int offset = getOffsetForPosition(event.getX(), event.getY());

        if (offset != -1) {
            TokenImageSpan[] links = text.getSpans(offset, offset, TokenImageSpan.class);

            if (links.length > 0) {
                links[0].onClick();
                handled = true;
            } else {
                //We didn't click on a token, so if any are selected, we should clear that
                clearSelections();
            }
        }
    }

    if (!handled && tokenClickStyle != TokenClickStyle.None) {
        handled = super.onTouchEvent(event);
    }
    return handled;

}

My code works, but my issue is that whenever I press an TAG at the end of it. It gets the next object. I assume that this is because I just use:

 val offset = getOffsetForPosition(event.x, event.y)
 if (offset != -1) {
     var offseted = text.substring(offset, text.length)
 }

When they use:

  if (offset != -1) {
      TokenImageSpan[] links = text.getSpans(offset, offset, TokenImageSpan.class);
  }

TokenImageSpan extends ImageSpan, so I can use it like that, But I do not know how to take the text from the ImageSpan. Any ideas how I can fix this please?


Solution

  • You can use clickable span as below:

    SpannableString ss = new SpannableString("your string comes 
    here");
    ClickableSpan clickableSpan = new ClickableSpan() {
    @Override
    public void onClick(View textView) {
        //do your stuff here on click
    }
    @Override
    public void updateDrawState(TextPaint ds) {
        super.updateDrawState(ds);
        ds.setUnderlineText(false);
    }
    };
    
    //set click range
    ss.setSpan(clickableSpan, 8, 15, 
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    
    //your text view or edittext
    textView.setText(ss);  
    textView.setMovementMethod(LinkMovementMethod.getInstance());
    textView.setHighlightColor(Color.TRANSPARENT);