find string at pixel position in textview [duplicate] - android

I have a TextView with an OnTouchListener. What I want is the character index the user is pointing to when I get the MotionEvent. Is there any way to get to the underlying font metrics of the TextView?

Have you tried something like this:
Layout layout = this.getLayout();
if (layout != null)
{
int line = layout.getLineForVertical(y);
int offset = layout.getOffsetForHorizontal(line, x);
// At this point, "offset" should be what you want - the character index
}
Hope this helps...

I am not aware of a simple direct way to do this but you should be able to put something together using the Paint object of the TextView via a call to TextView.getPaint()
Once you have the paint object you will have access to the underlying FontMetrices via a call to Paint.getFontMetrics() and have access to other functions like Paint.measureText() Paint.getTextBounds(), and Paint.getTextWidths() for accessing the actual size of the displayed text.

While it generally works I had a few problems with the answer from Tony Blues.
Firstly getOffsetForHorizontal returns an offset even if the x coordinate is way beyond the last character of the line.
Secondly the returned character offset sometimes belongs to the next character, not the character directly underneath the pointer. Apparently the method returns the offset of the nearest cursor position. This may be to the left or to the right of the character depending on what's closer by.
My solution uses getPrimaryHorizontal instead to determine the cursor position of a certain offset and uses binary search to find the offset underneath the pointer's x coordinate.
public static int getCharacterOffset(TextView textView, int x, int y) {
x += textView.getScrollX() - textView.getTotalPaddingLeft();
y += textView.getScrollY() - textView.getTotalPaddingTop();
final Layout layout = textView.getLayout();
final int lineCount = layout.getLineCount();
if (lineCount == 0 || y < layout.getLineTop(0) || y >= layout.getLineBottom(lineCount - 1))
return -1;
final int line = layout.getLineForVertical(y);
if (x < layout.getLineLeft(line) || x >= layout.getLineRight(line))
return -1;
int start = layout.getLineStart(line);
int end = layout.getLineEnd(line);
while (end > start + 1) {
int middle = start + (end - start) / 2;
if (x >= layout.getPrimaryHorizontal(middle)) {
start = middle;
}
else {
end = middle;
}
}
return start;
}
Edit: This updated version works better with unnatural line breaks, when a long word does not fit in a line and gets split somewhere in the middle.
Caveats: In hyphenated texts, clicking on the hyphen at the end of a line return the index of the character next to it. Also this method does not work well with RTL texts.

Related

How to swipe right (vertical) in particular box, and verify all results in Android with Appium

Scenario is swipe right to left at a box. and you will get some dynamic result. and verify them.
U can Identify locators for 2 elements -
1 - Element till where u want to reach
2 - A Common Xpath for All Horizontal Elements in that box and run following code to Scroll Horizontally.
public static void scrollTillElementHorizontally(By by, By allElements) {
if (driver.findElements(By) > 0 || driver.findElement(By).isDisplayed())
return;
int y = driver.findElement(allElements).getCenter().y;
int width = driver.manage().window().getSize().getWidth();
double startX = width * 0.80;
double endX = width * 0.20;
TouchAction action = new TouchAction(driver);
int i = 0;
while (i < 50) {
if (driver.findElements(By) > 0 || driver.findElement(By).isDisplayed())
break;
action.press(PointOption.point((int) startX, y)).waitAction(WaitOptions.waitOptions(Duration.ofSeconds(2)))
.moveTo(PointOption.point((int) endX, y)).release().perform();
i++;
}
}
And if u want to remove duplicate elements while scrolling add the elements to HashSet which would remove duplicates while scrolling.

TextView getLineCount()*getLineHeight() != getHeight()

I'm trying to get the exact Y position of a given line of text. I thought I would use i * TextView.getLineHeight() to get the Y position of the ith line, but that doesn't seem to be right.
The reason I think this is wrong is because TextView.getLineHeight() * TextView.getLineCount() != TextView.getHeight() which I assume is due to the same error. The line spacing and line multiplier are both default for this TextView.
What is the proper method to get the y position of the ith line in a TextView?
EDIT ---
It seems even TextView.getLayout().getLineTop(i) isn't correct, although please tell me if I'm overlooking something
private int getTextViewHeight(TextView textView) {
Layout layout = textView.getLayout();
int desired = layout.getLineTop(textView.getLineCount());
int padding = textView.getCompoundPaddingTop() +
textView.getCompoundPaddingBottom();
return desired + padding;
}
This code is helped.Read the TextView SourceCode(about onMeasure) you can know how it worked.

how to know that how many characters could be placed in one line?

I'm developing an android application and i need to know the number of characters that could be shown in one line to determine the number of lines of my string with some method .
How could do this ?
tanks a lot .
This should do the job for you (give or take a few mistakes from coding without an ide :-/ )
int countLineBreaks(final TextView view, final String toMeasure) {
final Paint paint = textView.getPaint(); // Get the paint used by the TextView
int startPos = 0;
int breakCount = 0;
final int endPos = toMeasure.length();
// Loop through the string, moving along the number of characters that will
// fit on a line in the TextView. The number of iterations = the number of line breaks
while (startPos < endPos) {
startPos += paint.breakText(toMeasure.substring(startPos, endPos),
true, tv.getWidth(),(float[]) null);
lineCount++;
}
// Line count will now equal the number of line-breaks the string will require
return lineCount;
}
..you get the idea ;-)
Every character has its own width.
As any String which contains 10 characters is not same to another String which also contains 10 char.
u can set it by --> textView.setTypeface(Typeface.MONOSPACE);
Monospace typeface works for making will regular char equal width.
Then u can do any stuff to get that how many characters could be placed in one line?
You can try this
set the Typeface.MONOSPACE as geet suggested. then do the following
Paint.measureText(String s) returns the width of the String s. By using this you can get the required width of 1 character. And from the View.getwidth() method you can get the width of the view. So from these two values you can calculate
try this:
int totalLine = textView.getMeasuredHeight() / textView.getLineHeight();

How to get the position of selected character or String in EditText

I want to know how can I get the x,y position of the selected character or String in
EditText. Is that possible?
Use this code to get the first index of a certain character:
String s = editText.getText().toString();
int position = s.indexOf("C"); // where C is your character to be searched
Here is how to get the x and y coordinates of a specific character in a TextView, should work for EditText as well. offset is the index of the desired character in the view's text.
Layout layout = editView.getLayout();
if (layout == null) { // Layout may be null right after change to the view
// Do nothing
}
int lineOfText = layout.getLineForOffset(offset);
int xCoordinate = (int) layout.getPrimaryHorizontal(offset);
int yCoordinate = layout.getLineTop(lineOfText);

Touch event on custom keyboard

I've created a customized keyboard of my own.
When I click & drag over the keyboard the key should be highlighted according to my finger move. When I lift up my finger the corresponding letter has to be printed on the EditText.
How could I do this?
Assuming you are just drawing your 'customized' keyboard onto a Canvas. Obviously using the built-in keyboard is better. However if you really want to do this yourself, here is a solution.
Draw each character of your keyboard, store it's x,y location in an array corresponding to each key as you draw it.
//initialise
int[] letterX = new int[29];
int[] letterY = new int[29];
char[] keyboardChar = {'Q','W','E','R','T','Y','U','I','O','P','A','S','D','F','G','H','J','K','L','Z','X','C','V','B','N','M',' ', '<','#'};
somewhere in your 'draw' method:
// first keyboard row (do this for each row moving adding to y for next row
x=10; y=50; keySpacing = 30; // starting x, y position
for ( int charIndex = 0; charIndex < 10; charIndex++) {
letterX[charIndex] = x;
letterY[charIndex] = y;
textWidth = mPaint.measureText(keyboardChar, charIndex, 1);
if ( !letterHit ) {
canvas.drawText(keyboardChar, charIndex, 1, x - (textWidth/2), y, mPaint);
} else {
// this key is being pressed so draw highlighted somehow
canvas.drawText(keyboardChar, charIndex, 1, x - (textWidth/2), y,mPaint);
}
x += keySpacing;
}
In the onTouchEvent, compare the x, y touch position to your array of letterX, letterY this will tell you if a key is being pressed. The array index tells you which character it is. You'd need some code in the draw method to print out the key highlighted if its being pressed (example below assumes 16px tolerance).
for (int j=0; j < 29; j++ ) {
if (( Math.abs(touchX - letterX[j]) < 16 ) && ( Math.abs(touchY - letterY[j]) < 16 ) && !keyboardLock ) {
letterHit = j;
}
}
You'll still need more logic (for deleting etc) and you'll need to maintain a string. That's why really this is best to use the system keyboard if possible.
I think you need something like this post .

Categories

Resources