Draw text in a specific rectangle

Using the canvas, I want to draw a short label text (1-2 characters) that fits into a specific rectangle. For some other reason, the scaling that I use is such that the dimensions of this reconnection are small, i.e. around 1.

The problem I am facing is to calculate the optimal (as much as possible so that the text still matches) text size that will be used with Paint.setTextSize before drawing the text (which I use with Canva.drawText() ) . To do this, I can either use the Paint.Fontmetrics object to get some common font sizes like float or getTextBounds(String text, int start, int end, Rect bounds) to get the bounding rectangle of the text as an integer rectangle. Due to the scaling that I use, the integer bounding box from the last is inaccurate to calculate the optimal text size for my purpose.

I need some method to get a bounding rectangle of text with higher precision (i.e. getStringBounds(String str, Graphics context) in java.awt.FontMetrics ), but I did not find a suitable method.

+4
source share
2 answers

I was also busy with this problem.

Unfortunately, I have not yet been able to establish the native source, but I'm sure that the text dimensions (both getTextBounds () and measureText ()) are very different from the real text output, especially for small font sizes (as you can see in the screenshot of Moritz ) I believe that measurement methods use the width of the float for char, and the actual text output uses int (presumably due to performance). This, of course, will fail if you need an absolutely accurate size (for example, for autosave). I experimented a bit with a monospace font. This image shows some of these experiments:

Android Autoscale experiments .

The text scale in these experiments was set by automatic scaling.

On the top line, I created the fields in integer increments. They correspond to the output of Android. You see, the last cipher does not fit the screen!

In the second line, the boxes were created in increments of the float. The boxes fit the screen better, but they don't match the Android output!

In the last line, the fields were incremented using a float increment, and the text was displayed char on char in the same increment. Both boxes and symbols are perfect.

In my conclusion, it is currently not possible to set the text output on Android to exact width. The problem does not seem to be in measurement methods that are reasonably accurate. The problem is that you cannot set the exact width of the text using setTextSize ().

Try to reproduce the following:

  float width = 100; // define a width which should be achieved m_textPaint.setTextSize( 100 ); // set a text size surely big enough Rect r = new Rect(); String s = new String("This is a test text"); m_textPaint.getTextBounds( s, 0, s.length(), r ); // measure the text with a random size float fac = width / r.width(); // compute the factor, which will scale the text to our target width Log.i( "MyTestOutput", "current text width:" + r.width() ); Log.i( "MyTestOutput", "wanted text width:" + width ); Log.i( "MyTestOutput", "factor:" + fac ); Log.i( "MyTestOutput", "expected result:" + (float) r.width() * fac ); m_textPaint.setTextSize( m_textPaint.getTextSize() * fac ); m_textPaint.getTextBounds( s, 0, s.length(), r ); // now final measurement: whats the real width? Log.i( "MyTestOutput", "real result:" + r.width() ); 

I get the output:

05-26 12: 18: 18.420: I / MyTestOutput (23607): current text width: 1125

05-26 12: 18: 18.425: I / MyTestOutput (23607): width of the required text: 100.0

05-26 12: 18: 18.425: I / MyTestOutput (23607): coefficient: 0.08888889

05-26 12: 18: 18.425: I / MyTestOutput (23607): expected result: 100.0

05-26 12: 18: 18.430: I / MyTestOutput (23607): actual result: 94

So, in my opinion, the only way to achieve very precisely autoscaled text is a) to autoscale it by measuring and setting the text size b) to display its char on char using its own calculation.

Unfortunately, this only works for monospace fonts. For other fonts this will be harder, but also possible.

+7
source

I did this a few days ago: See my blog .

You would use StaticLayout :

 // bmp1 is a source bitmap (in that case 44x44px big). // density is the screen density, you will need to retrieve it yourself as well. canvas.drawBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.overlay_44x44), new Matrix(), null); // Initialize using a simple Paint object. final TextPaint tp = new TextPaint(WHITE); // Save the canvas state, it might be re-used later. canvas.save(); // Create a padding to the left & top. canvas.translate(4*density, 4*density); // Clip the bitmap now. canvas.clipRect(new Rect(0, 0, bmp1.getWidth(),(int) (bmp1.getHeight() - (6*density)))); // Basic StaticLayout with mostly default values StaticLayout sl = new StaticLayout(message, tp, bmp1.getWidth(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); sl.draw(canvas); // Restore canvas. canvas.restore(); 
+1
source

Source: https://habr.com/ru/post/1334315/


All Articles