MOBILE

Android Application Development : Rolling Your Own Widgets (part 3) - Canvas Drawing - Drawing text, Matrix transformations

11/19/2013 8:10:14 PM
2.1. Drawing text

The most important Canvas methods are those used to draw text. Although some Canvas functionality is duplicated in other places, text-rendering capabilities are not. In order to put text in your widget, you will have to use the Canvas (or, of course, subclass some other widget that uses it).

Canvas methods for rendering text come in pairs: three sets of two signatures. Example 4 shows one of the pairs.

Example 4. A pair of text drawing methods
public void drawText(String text, float x, float y, Paint paint)
public void drawText(char[] text, int index, int count, float x, float y, Paint paint)


There are several pairs of methods. In each pair, the first of the two methods in the pair uses String, and the second uses three parameters to describe the text: an array of char, the index indicating the first character in that array to be drawn, and the number of total characters in the text to be rendered. In some cases, there are additional convenience methods.

Example 5 contains an onDraw method that demonstrates the use of the first style of each of the three pairs of text rendering methods. The output is shown in Figure 2.

Example 5. Three ways of drawing text
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);

Paint paint = new Paint();

paint.setColor(Color.RED);
canvas.drawText("Android", 25, 30, paint);

Path path = new Path();
path.addArc(new RectF(10, 50, 90, 200), 240, 90);
paint.setColor(Color.CYAN);
canvas.drawTextOnPath("Android", path, 0, 0, paint);

float[] pos = new float[] {
20, 80,
29, 83,
36, 80,
46, 83,
52, 80,
62, 83,
68, 80
};
paint.setColor(Color.GREEN);
canvas.drawPosText("Android", pos, paint);
}

Figure 2. Output from three ways of drawing text


As you can see, the most elementary of the pairs, drawText, simply draws text at the passed coordinates. With DrawTextOnPath, on the other hand, you can draw text along any Path. The example path is just an arc. It could just as easily have been a line drawing or a Bezier curve.

For those occasions on which even DrawTextOnPath is insufficient, Canvas offers DrawPosText, which lets you specify the exact position of each character in the text. Note that the character positions are specified by alternating array elements: x1,y1,x2,y2, and so on.

2.2. Matrix transformations

The second interesting group of Canvas methods are the Matrix transformations and their related convenience methods, rotate, scale, and skew. These methods transform what you draw in ways that will immediately be recognizable to those familiar with 3D graphics. They allow a single drawing to be rendered in ways that can make it appear as if the viewer were moving with respect to the objects in the drawing.

The small application in Example 6 demonstrates the Canvas’s coordinate transformation capabilities.

Example 6. Using a Canvas
import android.app.Activity;

import android.content.Context;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;

import android.os.Bundle;

import android.view.View;

import android.widget.LinearLayout;


public class TranformationalActivity extends Activity {

private interface Transformation {
void transform(Canvas canvas);
String describe();
}

private static class TransfomedViewWidget extends View {
private final Transformation transformation;

public TransfomedViewWidget(Context context, Transformation xform) {
super(context);

transformation = xform;

setMinimumWidth(160);
setMinimumHeight(105);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
getSuggestedMinimumWidth(),
getSuggestedMinimumHeight());
}

@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);

Paint paint = new Paint();

canvas.save();
transformation.transform(canvas);

paint.setTextSize(12);
paint.setColor(Color.GREEN);
canvas.drawText("Hello", 40, 55, paint);

paint.setTextSize(16);
paint.setColor(Color.RED);
canvas.drawText("Android", 35, 65, paint);

canvas.restore();

paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
Rect r = canvas.getClipBounds();
canvas.drawRect(r, paint);

paint.setTextSize(10);
paint.setColor(Color.BLUE);
canvas.drawText(transformation.describe(), 5, 100, paint);
}

}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.transformed);

LinearLayout v1 = (LinearLayout) findViewById(R.id.v_left);
v1.addView(new TransfomedViewWidget(
this,
new Transformation() {
@Override public String describe() { return "identity"; }
@Override public void transform(Canvas canvas) { }
} ));
v1.addView(new TransfomedViewWidget(<-- (9)
this,
new Transformation() {<-- (10)
@Override public String describe() { return "rotate(-30)"; }
@Override public void transform(Canvas canvas) {
canvas.rotate(-30.0F);
} }));
v1.addView(new TransfomedViewWidget(<-- (9)
this,
new Transformation() {<-- (10)
@Override public String describe() { return "scale(.5,.8)"; }
@Override public void transform(Canvas canvas) {
canvas.scale(0.5F, .8F);
} }));
v1.addView(new TransfomedViewWidget(<-- (9)
this,
new Transformation() {<-- (10)
@Override public String describe() { return "skew(.1,.3)"; }
@Override public void transform(Canvas canvas) {
canvas.skew(0.1F, 0.3F);
} }));

LinearLayout v2 = (LinearLayout) findViewById(R.id.v_right);
v2.addView(new TransfomedViewWidget(
this,
new Transformation() {<-- (10)
@Override public String describe() { return "translate(30,10)"; }
@Override public void transform(Canvas canvas) {
canvas.translate(30.0F, 10.0F);
} }));
v2.addView(new TransfomedViewWidget(<-- (12)
this,
new Transformation() {<-- (10)
@Override public String describe()
{ return "translate(110,-20),rotate(85)"; }
@Override public void transform(Canvas canvas) {
canvas.translate(110.0F, -20.0F);
canvas.rotate(85.0F);
} }));
v2.addView(new TransfomedViewWidget(<-- (12)
this,
new Transformation() {<-- (10)
@Override public String describe()
{ return "translate(-50,-20),scale(2,1.2)"; }
@Override public void transform(Canvas canvas) {
canvas.translate(-50.0F, -20.0F);
canvas.scale(2F, 1.2F);
} }));
v2.addView(new TransfomedViewWidget(<-- (12)
this,
new Transformation() {<-- (10)
@Override public String describe() { return "complex"; }
@Override public void transform(Canvas canvas) {
canvas.translate(-100.0F, -100.0F);
canvas.scale(2.5F, 2F);
canvas.skew(0.1F, 0.3F);
} }));
}
}

The results of this protracted exercise are shown in Figure 3.

Here are some of the highlights of the code:

Figure 3. Transformed views


This small application introduces several new ideas and demonstrates the power of Android graphics for maintaining state and nesting changes.

The application defines a single widget, TransformedViewWidget, of which it creates eight instances. For layout, the application creates two views named v1 and v2, retrieving their parameters from resources. It then adds four instances of TransformedViewWidget to each LinearLayout view. This is an example of how applications combine resource-based and dynamic views. Note that the creation both of the layout views and the new widgets take place within the Activity’s onCreate method.

This application also makes the new widget flexible through a sophisticated division of labor between the widget and its Transformation. Several simple objects are drawn directly within the definition of TransformedViewWidget, in its onDraw method:

  • A white background

  • The word “Hello” in 12-point green type

  • The word “Android” in 16-point red type

  • A black frame

  • A blue label

In the middle of this, the onDraw method performs a transformation specified at its creation. The application defines its own interface, called Transformation, and the constructor for TransformedViewWidget accepts a Transformation as a parameter. We’ll see in a moment how the caller actually codes a transformation.

It’s important to see first how the widget onDraw preserves its own text from being affected by the Transformation. In this example, we want to make sure that the frame and label are drawn last, so that they are drawn over anything else drawn by the widget, even if they might overlap. On the other hand, we do not want the transformation applied earlier to affect them.

Fortunately, the Canvas maintains an internal stack onto which we can record and recover the translation matrix, clip rectangle, and many other elements of mutable state in the Canvas. Taking advantage of this stack, onDraw calls save to preserve its state before the transformation, and restore afterward to recover the saved state.

The rest of the application controls the transformation used in each of the eight instances of TransformedViewWidget. Each new instance of the widget is created with its own anonymous instance of Tranformation. The image in the area labeled “identity” has no translation applied. The other seven areas are labeled with the transformations they demonstrate.

The base methods for Canvas translation are setMatrix and concatMatrix. These two methods allow you to build any possible transformation. The getMatrix method allows you to recover a dynamically constructed matrix for later use. The methods introduced in the example—translate, rotate, scale, and skew—are convenience methods that compose specific, constrained matrixes into the current Canvas state.

Although it may not be obvious at first, these transformation functions can be tremendously useful. They allow your application to appear to change its point of view with respect to a 3D object. It doesn’t take too much imagination, for instance, to see the scene in the square labeled “scale(.5,.8)” as the same as that seen in the square labeled “identity”, but viewed from farther away. With a bit more imagination, the image in the box labeled “skew(.1,.3)” again could be the untransformed image, but this time viewed from above and slightly to the side. Scaling or translating an object can make it appear to a user as if the object has moved. Skewing and rotating can make it appear that the object has turned. We will make good use of this technique in animation.

When you consider that these transformation functions apply to everything drawn on a canvas—lines, text, and even images—their importance in applications becomes even more apparent. A view that displays thumbnails of photos could be implemented trivially, though perhaps not optimally, as a view that scales everything it displays to 10% of its actual size. An application that simulates what you see as you look to your left while driving down the street might be implemented in part by scaling and skewing a small number of images.

Other  
  •  iPhone SDK 3 Programming : XML Processing - An RSS Reader Application
  •  iPhone SDK 3 Programming : XML Processing - Simple API for XML (SAX)
  •  iPhone SDK 3 Programming : XML Processing - Document Object Model (DOM)
  •  iPhone SDK 3 Programming : XML and RSS
  •  Windows Phone 8 : Making Money - Modifying Your Application, Dealing with Failed Submissions, Using Ads in Your Apps
  •  Windows Phone 8 : Making Money - Submitting Your App (part 3) - After the Submission
  •  Windows Phone 8 : Making Money - Submitting Your App (part 2) - The Submission Process
  •  Windows Phone 8 : Making Money - Submitting Your App (part 1) - Preparing Your Application
  •  Windows Phone 8 : Making Money - What Is the Store?
  •  BlackBerry Push APIs (part 3) - Building an Application that Uses the BlackBerry Push APIs - Checking the Status of a Push Request and Cancelling a Push Request
  •  
    Top 10
    Review : Sigma 24mm f/1.4 DG HSM Art
    Review : Canon EF11-24mm f/4L USM
    Review : Creative Sound Blaster Roar 2
    Review : Philips Fidelio M2L
    Review : Alienware 17 - Dell's Alienware laptops
    Review Smartwatch : Wellograph
    Review : Xiaomi Redmi 2
    Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
    Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
    3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
    REVIEW
    - First look: Apple Watch

    - 3 Tips for Maintaining Your Cell Phone Battery (part 1)

    - 3 Tips for Maintaining Your Cell Phone Battery (part 2)
    VIDEO TUTORIAL
    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
    Popular Tags
    Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8