Locate text position in TextView - android

How can I calculate the position X , Y of a letter in a TextView by knowing it's index?
I need this to show a graphical pointer to the touched letter or word but I don't know how to locate the right place.

May this help you:
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...

Try this:
Rect bounds = new Rect();
Paint textPaint = textView.getPaint();
textPaint.getTextBounds(text,0,text.length(),bounds);
int height = bounds.height();
int width = bounds.width();
That should give you the height and width. Create an array of height/widths with a different value for each individual character, you could do it quickly if you used a loop and ASCII character codes. Then, you could add the widths of each character until you get to the one you want.
But there's what I think is an easier way. Instead of a TextView, try a LinearLayout with multiple textviews. If you use a textview for each character, you can just use getX and getY.

Related

MPAndroidChart gradient line depending on dataset values

I'm currently working on a project using MPAndroidChart and i want to make a LineChart to represent data per hour. I need this chart to have gradient line depending on values on yAxis.
eg. In the graph above i want the line to change the color when yAxis values are >50.
I didn't find any solution to my problem, so any suggestions or examples are welcome
I've recently had to face the exact same problem, and the solution was to define a linear gradient and apply it as a shader, like this:
(Warning: You'll notice that the code looks funny, and that is because it is xamarin code (i.e: C#, not java nor kotlin), but you'll be able to translate it easily, mostly you have to change the PascalCase for camelCase, and change some properties into setters and getter or vice versa, just use your IDE's intellisense)
[set your line chart, add the dataset, etc]
...
var gradient = new LinearGradient(0f, 0f, 0f, 100f,
[your first color], [your second color],
Shader.TileMode.Clamp);
var paint = vh.Chart.Renderer.PaintRender;
paint.SetShader(gradient);
...
[set axis, labels, grid, etc]
...
lineChart.Invalidate(); //this is to refresh the chart on the screen, you may need it or not depending on your code
In the code above you just have to add your colors, and you may want to change the gradient direction (I'm using a vertical gradient here) by changing the 4 first floats when creating the linearGradient. One typical change is to set the y0 = 0 and y1 = the height of your chart, for example. You have to play with the values according to your layout.
The result for this code is something like:
Of course, I'm showing it with a repeated sample dataset, that's why you are seeing two cards with the same line.
UPDATE:
I understand what you want to achieve, and with the code above you can do it. You will need some calculations, of course, but you can set the "trigger" where the color starts to change at any Y coordinate you want.
For example, in this first picture, I've used the coordinates 0,0,0,100
(Please keep in mind that I'm showing you the last value in dp, but in the real code I am translating it to the equivalent in pixels according to the device's resolution. It is roughly the height of my chart)
And in the following image, I've changed it to 0,0,0,50:
As you can see, you have full control over how the gradient is shown just by changing the 4 values. (or, in my case, just one of them)
(You may notice that I've changed the colors as per my design, it has nothing to do with the threshold)
Lets say you are using for-loop to populate your data. In list you need to populate a list of colors for every value and then use lineDataSet.setColors(listOfColors).
List<Integer> listOfColors = new List<>();
for(i= 0; i<dataEntries.size(); i++){
//Logic for colors
if(i<= dataEntries.size()-1){ //Otherwise app will crash on last index
if(dataEntries.get(i+1).y > 50)
listOfColors.add(Color.GREEN);
else
listOfColors.add(Color.RED);
}
Then in the last
lineDataSet.setColors(listOfColors)
Hope, this will help you.

Auto set rectangle length on canvas as per string length

I have an array of strings and I have drawn those strings on screen using android graphics library (Canvas, Paint). Firstly, a transparent rectangle is drawn then on this rectangle I paint the strings, giving the impression of text with border line and filled.
The problem is some strings are long and some are short, how can I modify the rectangle so that it is as long and as wide as the string is? (Like WRAP_CONTENT in textView)
Currently I am using this method
canvas.drawRect(_x-10, _y-10, _x+620, _y+30, rectanglePaint);
canvas.drawText(placeName, _x, _y, textPaint); //text
If there is a better way then please do let me know.
Note: It will be used in an AR app so the text will be moving from left to right and vice versa as the mobile is moving. _x, _y for place name works perfectly in 2nd line, I want text should remain highlighted, no matter how the mobile is moving.
Paint has a measure text method that will give the width of the text if it were to be drawn with that paint. Its get font metrics will probably give you some data that you can calculate the height from. The sizing, positioning, wrapping and rendering of text is a very complicated problem and I would advise using a child TextView (with a background) in your ViewGroup rather than trying to roll your own if you're doing anything complicated.

How can I anchor my DynamicLayout to the bottom of my canvas?

I am working on a custom View that includes frequently updated text that I want to anchor to the bottom of the canvas.
The text length changes as well, and I'd like it to wrap, moving up the screen as more lines are needed. (DynamicLayout thus seems like a solid choice for automating this)
However, I don't see any options in the docs about specifying where on my canvas the text is drawn or in which direction it should "grow".
Here is my initialization:
TextPaint subtleTextPaint = new TextPaint();
DynamicLayout dl = new DynamicLayout(text,subtleTextPaint,getWidth()
,Layout.Alignment.ALIGN_CENTER,1,0,true);
And in onDraw(), I simply pass the canvas to the DynamicLayout object like so:
dl.draw(c);
Right now, the text is drawn at the very top of the screen and word-wraps downwards as the text gets longer.
After quite a bit of searching, I found nothing that did what I wanted. So, in true hacker style, I created my own solution. By extending the DynamicLayout class and overwriting the getLineTop() function, I was able to achieve the functionality I was looking for.
I've posted the source code here.
What i think 1st you get the Width of the screen and then set the Text with the X y according to your screen dimension
that will work
snippet is following
width=canvas.getWidth();
if(width==720)
{
canvas.drawText(String, x , y, drawText);
}
else if(width==480)
{
canvas.drawText(String, x , (y, drawText);
}
else
{
canvas.drawText(String, x , (y, drawText);
}

Android: Measure Text Height on a Canvas

I am currently working on rendering a Bitmap, that I then want to send to a mobile printer. However, I am struggling with measuring the height of my text, so I can advance the y position appropriately.
My basic bitmap/canvas/paint configuration is this (Font Size is 16 and the dimensions of the bitmap are 200x400 (width x height):
public MyRenderer() {
// Initialize bitmap
bitmap = Bitmap.createBitmap(200, 400, Bitmap.Config.ARGB_8888);
// Initialize canvas
canvas = new Canvas(bitmap);
// Initialize brush (Paint instance)
brush = new Paint();
brush.setTextSize(16);
brush.setTypeface(Typeface.SANS_SERIF);
brush.setColor(Color.BLACK);
brush.setStyle(Paint.Style.FILL);
brush.setAntiAlias(true);
brush.setTextAlign(Align.LEFT);
}
So far so good, now what I want to do is: If I use the Paint's method drawText I need to supply the x and y coordinates. As for x that's zero (assuming left aligned text) but as for y, I'd have to calculate the height of each text I print and add it up, so I can keep track of my current y position.
And this is where it gets odd: I am using the following method to determine the height of a text (using the Paint objected that I initialized previously - it's called "brush"):
public int measureHeight(String text) {
Rect result = new Rect();
// Measure the text rectangle to get the height
brush.getTextBounds(text, 0, text.length(), result);
return result.height();
}
The above method returns the following values for the following texts:
"Hello World" returns a height of 12
"A camera instance can be used to compute 3D transformations and generate a matrix." returns a height of 16
"Introducing Android Design: The place to learn about principles, building blocks, and patterns for creating world-class Android user interfaces. Whether you're a UI professional or a developer playing that role, these docs show you how to make good design decisions, big and small." returns a height of 16
It makes sense to me, that number 2 and 3 return a greater height than number 1 but if one line has a height of 12 (as number one does) - it makes no sense, that multiple lines have a height of 16 ?
Am I missing something here? There is a convenience method for measuring the width of a text (using an instance of paint and call measureText("myText") which works perfectly, however I am quite at a loss, when it comes to the height, as the above given results don't make any sense to me.
EDIT
I am aware, that getTextBounds probably does no auto-wrapping of multi-lined text, and that's ok, I already wrote a method for splitting text, but even if it just measures one line, the above given length values still seem unlikely.
I think it is because the "p" in "compute" extends below the baseline whereas "Hello World" only contains letters that are above the baseline.
Since the line distance should not depend on what specific letters your text happens to consist of you are probably looking for Paint.FontMetrics which can be obtained via Paint.getFontMetrics(). Compute descent - ascent + leading to get the recommended baseline distance (because ascent has a negative value).
There is a small error in the accepted answer. If you want the text height, you should use
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float textHeight = fm.descent - fm.ascent;
And if you want the line height, you should use
float lineHeight = fm.bottom - fm.top + fm.leading;
Leading is optional interline spacing, so if you need to get the line hight you can include it. But if you just want the text height, then you can leave it off.
Note
I've never actually seen leading be anything else than 0, and as far as I can tell it even seems to be ignored in the TextView source code (and its associated Layout, StaticLayout, etc.). Please correct me if I'm wrong. So it is probably safe it leave it out of line hight calulations, but I'm not completely sure about that.
See also
Getting text height from getTextBounds vs FontMetrics vs StaticLayout
Meaning of top, ascent, baseline, descent, bottom, and leading in Android's FontMetrics

Divide String over 2 custom Rect shapes in onDraw();

I'm having issues dividing my String object over 2 Rect shapes.
Firstly i have no idea how to set canvas.drawtext to a specified region.
Like you would do if you use textview.
Secondly I can only calculate / break my string at the width of the Rect shape.
meaning I can only draw 1 line instead of filling out the box...
I could take the box height and divide it by the textsize to calc the number of lines that fit,
but then i would have to draw line by line ?
surely there must be examples for this , but i cannot seem to find them.
somewhat the situation :
View :
[xxxxx]----------
[xxxxx]----------
----------[xxxxx]
----------[xxxxx]
i want a very long string to be divided and placed at the [xxx] boxes
ta.

Categories

Resources