Text can be considered a primitive from the point of view of Canvas operations, but we've put it here on its own section, as it's quite important. Instead of starting with the simplest example, as we've just introduced Paths, we'll continue our previous example and draw the text on top of the Path. To draw the text, we'll reuse the Paint object for the Bezier curve, but we'll add some text parameters:
pathPaint.setTextSize(50.f); pathPaint.setTextAlign(Paint.Align.CENTER);
This sets the size of the text and also aligns it to the center of the Path, so every time we add new points, the text position will adapt to remain at the center. To draw the text, we simply call the drawTextOnPath() method:
canvas.drawTextOnPath("Building Android UIs with Custom Views", path, 0, 0, pathPaint);
This was a very quick addition to our code, but if we execute our application, we can see the results with the text over the Path lines:
Take into account that we're drawing the same things as we drawn before, but we can freely use the Path just as the guide for the text. There is no need to draw it or to draw the control points.
Check the full source code of this example on the Example19-Text folder, in the GitHub repository.
We've started drawing text on Paths, as we had the example almost built. However, there are more simple methods for drawing text. For instance, we can just draw a text on a specific position of the screen by calling either canvas.drawText(String text, float x, float y, Paint paint) or canvas.drawText(char[] text, float x, float y, Paint paint).
These methods will just do their job, but they will not check if the text fits in the available space and they will definitely not split and wrap the text. To do so, we'll have to do it ourselves. The Paint class provides us with methods to measure text and calculate text boundaries. For example, we created a small helper method that returns the width and height of String:
private static final float[] getTextSize(String str, Paint paint) { float[] out = new float[2]; Rect boundaries = new Rect(); paint.getTextBounds(str, 0, str.length(), boundaries); out[0] = paint.measureText(str); out[1] = boundaries.height(); return out; }
We've used the text boundary to get the text height, but we've used the measureText() method to get the text width. There are some differences on how the size is computed in both methods. Although it's not currently properly documented on the Android documentation site, there is an old discussion about this on Stack Overflow:
http://stackoverflow.com/questions/7549182/android-paint-measuretext-vs-gettextbounds.
However, we shouldn't implement our own text splitting method. If we want to draw a large text and we know it might need splitting and wrapping, we can use the StaticLayout class. In the example here, we'll create a StaticLayout with the width of half the view width.
We can implement it on our onLayout() method:
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); // create a layout of half the width of the View if (layout == null) { layout = new StaticLayout( LONG_TEXT, 0, LONG_TEXT.length(), paint, (right - left) / 2, Layout.Alignment.ALIGN_NORMAL, 1.f, 1.f, true); } }
In our onDraw() method, we draw it centered on the screen. As we know, the layout width was half the view width; we know we have to displace it by a quarter of the width.
@Override protected void onDraw(Canvas canvas) { canvas.drawColor(BACKGROUND_COLOR); canvas.save(); // center the layout on the View canvas.translate(canvas.getWidth()/4, 0); layout.draw(canvas); canvas.restore(); }
Here is the result:
Check the full source code of this example in the Example20-Text folder, in the GitHub repository.