How get coordinate of a ClickableSpan inside a Tex

2019-01-17 22:50发布

问题:

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.

回答1:

I have found a solution :-) Here it is, may be this will help other persons with the same problem than 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
);