How get coordinate of a ClickableSpan inside a TextView?
Asked Answered
J

2

16

I have a TextView with many ClickableSpan.

On click on a ClickableSpan, I have to get the coordinate on screen of it (to show a custom View at his position).

The problem is that I have no idea of how I can do this. The onClick() method of the ClickableSpan gives me in parameter a View, the TextView which contains the ClickableSpan.

I have used the following to get characters position in the TextView, but I don't know how I can convert it to get x/y position on screen of the text.

@Override
public void onClick(View v){

    SpannableString completeText = (SpannableString)((TextView) v).getText();
    Log.v("characters position", completeText.getSpanStart(this) + " / " + completeText.getSpanEnd(this));

}

Thanks in advance for your help!

EDIT : I want to get the entire coordinate of the ClickableSpan and its size, the aim is to show my custom view at the bottom center of the text. The onTouch method will give me the finger position, not the entire text coordinates. So, with this method, I will not have the middle of the text.

Jumbala answered 10/8, 2012 at 16:12 Comment(2)
Override onTouch() and use event.getX(), event.getY()?Mixer
Thanks for your answer ! Unfortunately, I found this method very... ugly ! I hope there is a better solution. Moreover, I have forgotten to give another precision : I want to get the entire coordinate of the ClickableSpan and its size, the aim is to show my custom view at the bottom center of the text. The onTouch method will give me the finger position, not the entire text coordinates. So, with this method, I will not have the middle of the text. Any other idea ?Yoicks
J
34

I have found a solution :-) Here it is, may be this will help others with the same problem like me !

// Initialize global value
this.parentTextViewRect = new Rect();
        
        
// Initialize values for the computing of clickedText position
SpannableString completeText = (SpannableString)(parentTextView).getText();
Layout textViewLayout = parentTextView.getLayout();
        
double startOffsetOfClickedText = completeText.getSpanStart(clickedText);
double endOffsetOfClickedText = completeText.getSpanEnd(clickedText);
double startXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal((int)startOffsetOfClickedText);
double endXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal((int)endOffsetOfClickedText);
        
        
// Get the rectangle of the clicked text
int currentLineStartOffset = textViewLayout.getLineForOffset((int)startOffsetOfClickedText);
int currentLineEndOffset = textViewLayout.getLineForOffset((int)endOffsetOfClickedText);
boolean keywordIsInMultiLine = currentLineStartOffset != currentLineEndOffset;
textViewLayout.getLineBounds(currentLineStartOffset, this.parentTextViewRect);
        
        
// Update the rectangle position to his real position on screen
int[] parentTextViewLocation = {0,0};
parentTextView.getLocationOnScreen(parentTextViewLocation);
        
double parentTextViewTopAndBottomOffset = (
    parentTextViewLocation[1] - 
    parentTextView.getScrollY() + 
    this.parentTextView.getCompoundPaddingTop()
);
this.parentTextViewRect.top += parentTextViewTopAndBottomOffset;
this.parentTextViewRect.bottom += parentTextViewTopAndBottomOffset;
        
// In the case of multi line text, we have to choose what rectangle take
if (keywordIsInMultiLine){
            
    int screenHeight = this.mWindowManager.getDefaultDisplay().getHeight();
    int dyTop = this.parentTextViewRect.top;
    int dyBottom = screenHeight - this.parentTextViewRect.bottom;
    boolean onTop = dyTop > dyBottom;
            
    if (onTop){
        endXCoordinatesOfClickedText = textViewLayout.getLineRight(currentLineStartOffset);
    }
    else{
        this.parentTextViewRect = new Rect();
        textViewLayout.getLineBounds(currentLineEndOffset, this.parentTextViewRect);
        this.parentTextViewRect.top += parentTextViewTopAndBottomOffset;
        this.parentTextViewRect.bottom += parentTextViewTopAndBottomOffset;
        startXCoordinatesOfClickedText = textViewLayout.getLineLeft(currentLineEndOffset);
    }
            
}
        
this.parentTextViewRect.left += (
    parentTextViewLocation[0] +
    startXCoordinatesOfClickedText + 
    this.parentTextView.getCompoundPaddingLeft() - 
    parentTextView.getScrollX()
);
this.parentTextViewRect.right = (int) (
    this.parentTextViewRect.left + 
    endXCoordinatesOfClickedText - 
    startXCoordinatesOfClickedText
);
Jumbala answered 13/8, 2012 at 12:33 Comment(0)
A
0

try this:

inside of CustomSpannableString class onClick function

@Override
public void onClick(View v){
    val textView = v as TextView
    val s = textView.text as Spanned
    val startCoordinates = s.getSpanStart(this)
    val endCoordinates = s.getSpanEnd(this)
    return  arrayOf(startCoordinates, endCoordinates)
}

not in spannableString class

val textView = findView...   // txtView which text is set to SpannableString
val s = textView.text as Spanned 
// CustomSpannableStringObj of type CustomSpannableString 
val startCoordinates = s.getSpanStart(CustomSpannableStringObj)
val endCoordinates = s.getSpanEnd(CustomSpannableStringObj)
Averi answered 17/9, 2021 at 14:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.