Creating a Compass View Example

Appportunity

Build your own Android App Dev Empire

Get Instant Access

In the following example you'll create a new Compass View by extending the View class. This View will display a traditional compass rose to indicate a heading/orientation. When complete, it should appear as in Figure 4-2.

Android Drawcircle Drawline Example
FIGURE 4-2

A compass is an example of a UI control that requires a radically different visual display from the Text Views and Buttons available in the SDK toolbox, making it an excellent candidate for building from scratch.

In Chapter 14 you'll use this Compass View and the device's built-in accelerometer to display the user's current bearing. Then in Chapter 15 you will learn some advanced techniques for Canvas drawing that will let you dramatically improve its appearance.

1. Create a new Compass project that will contain your new Compass View, and create an Activity to display it. Now create a new CompassView class that extends View. Create constructors that will allow the View to be instantiated either in code or through inflation from a resource layout. Add a new initCompassView method that will be used to initialize the control and call it from each constructor.

package com.paad.compass;

import android.content.Context;

import android.graphics.*;

import android.graphics.drawable.*;

import android.view.*;

import android.util.AttributeSet;

import android.content.res.Resources;

public class CompassView extends View { public CompassView(Context context) { super(context); initCompassView();

public CompassView(Context context, AttributeSet attrs) { super(context, attrs); initCompassView();

public CompassView(Context context, AttributeSet ats, int defaultStyle) { super(context, ats, defaultStyle); initCompassView();

protected void initCompassView() { setFocusable(true);

2. The compass control should always be a perfect circle that takes up as much of the canvas as this restriction allows. Override the onMeasure method to calculate the length of the shortest side, and use setMeasuredDimension to set the height and width using this value.

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // The compass is a circle that fills as much space as possible. // Set the measured dimensions by figuring out the shortest boundary, // height or width.

int measuredWidth = measure(widthMeasureSpec); int measuredHeight = measure(heightMeasureSpec);

int d = Math.min(measuredWidth, measuredHeight);

setMeasuredDimension(d, d);

private int measure(int measureSpec) { int result = 0;

// Decode the measurement specifications.

int specMode = MeasureSpec.getMode(measureSpec);

int specSize = MeasureSpec.getSize(measureSpec);

if (specMode == MeasureSpec.UNSPECIFIED) {

// Return a default size of 200 if no bounds are specified. result = 200; } else {

// As you want to fill the available space // always return the full available bounds, result = specSize;

return result;

3. Create two new resource files that store the colors and text strings you'll use to draw the compass.

3.1. Create the text string resource res/values/strings.xml.

<?xml version="1.0" encoding="utf-8"?> <resources>

<string name="app_name">Compass</string> <string name="cardinal_north">N</string> <string name="cardinal_east">E</string> <string name="cardinal_south">S</string> <string name="cardinal_west">W</string> </resources>

3.2. Create the color resource res/values/colors.xml.

<?xml version="1.0" encoding="utf-8"?> <resources>

<color name="background_color">#F555</color> <color name="marker_color">#AFFF</color> <color name="text_color">#AFFF</color> </resources>

4. Now return to the Compassview class. Add a new property to store the displayed bearing, and create get and set methods for it.

private float bearing;

public void setBearing(float _bearing) { bearing = _bearing;

public float getBearing() { return bearing;

5. Next, return to the initCompassview method and get references to each resource created in Step 3. Store the string values as instance variables, and use the color values to create new class-scoped Paint objects. You'll use these objects in the next step to draw the compass face.

private Paint markerPaint; private Paint textPaint; private Paint circlePaint; private String northString; private String eastString; private String southString; private String westString; private int textHeight;

protected void initCompassView() { setFocusable(true);

circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

circlePaint.setColor(r.getColor(R.color.background_color));

circlePaint.setStrokeWidth(l);

circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);

Resources r = this.getResources(); northString = r.getString(R.string.cardinal_north); eastString = r.getString(R.string.cardinal_east); southString = r.getString(R.string.cardinal_south); westString = r.getString(R.string.cardinal_west);

textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint.setColor(r.getColor(R.color.text_color));

textHeight = (int)textPaint.measureText("yY");

markerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); markerPaint.setColor(r.getColor(R.color.marker_color));

6. The final step is drawing the compass face using the String and Paint objects you created in Step 5. The following code snippet is presented with only limited commentary. You can find more detail about drawing on the Canvas and using advanced Paint effects in Chapter 15.

6.1. Start by overriding the onDraw method in the CompassView class.

©Override protected void onDraw(Canvas canvas) {

6.2. Find the center of the control, and store the length of the smallest side as the compass's radius.

int px = getMeasuredWidth() / 2; int py = getMeasuredHeight() /2 ;

6.3. Draw the outer boundary, and color the background of the compass face using the drawCircle method. Use the circlePaint object you created in Step 5.

// Draw the background canvas.drawCircle(px, py, radius, circlePaint);

6.4. This compass displays the current heading by rotating the face so that the current direction is always at the top of the device. To achieve this, rotate the canvas in the opposite direction to the current heading.

// Rotate our perspective so that the 'top' is // facing the current bearing. canvas.save();

canvas.rotate(-bearing, px, py);

6.5. All that's left is to draw the markings. Rotate the canvas through a full rotation, drawing markings every 15 degrees and the abbreviated direction string every 45 degrees.

int textWidth = (int)textPaint.measureText("W");

int cardinalX = px-textWidth/2;

int cardinalY = py-radius+textHeight;

// Draw the marker every 15 degrees and text every 45. for (int i = 0; i < 24; i++) { // Draw a marker.

canvas.drawLine(px, py-radius, px, py-radius+10, markerPaint); canvas.save();

canvas.translate(0, textHeight);

String dirString = "";

case(6)

case(12)

case(18)

dirString = northString; int arrowY = 2*textHeight; canvas.drawLine(px, arrowY, px-5, markerPaint); canvas.drawLine(px, arrowY, px+5, markerPaint);

break;

dirString = eastString; break; dirString = southString; break; dirString = westString; break;

3*textHeight, 3*textHeight,

canvas.drawText(dirString, cardinalX, cardinalY, textPaint);

// Draw the text every alternate 45deg

String angle = String.valueOf(i*15);

float angleTextWidth = textPaint.measureText(angle);

int angleTextX = (int)(px-angleTextWidth/2); int angleTextY = py-radius+textHeight;

canvas.drawText(angle, angleTextX, angleTextY, textPaint);

canvas.restore(); canvas.rotate(15, px, py);

canvas.restore();

7. To view the compass, modify the main.xml layout resource and replace the TextView reference with your new CompassView. This process is explained in more detail in the next section.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.paad.compass.CompassView android:id="@+id/compassView" android:layout_width="fill_parent" android:layout_height="fill_parent"

</LinearLayout>

8. Run the Activity, and you should see the CompassView displayed. See Chapter 14 to learn how to bind the CompassView to the device's compass.

All code snippets in this example are part of the Chapter 4 Compass project, available for download at Wrox.com.

Was this article helpful?

+3 -2
Mobile Apps Made Easy

Mobile Apps Made Easy

Quick start guide to skyrocket your offline and online business success with mobile apps. If you know anything about mobile devices, you’ve probably heard that famous phrase coined by one of the mobile device’s most prolific creators proclaiming that there’s an app for pretty much everything.

Get My Free Training Guide


Post a comment