Login     Sign Up
Cyril Sermon (@admin)
9 months ago
68 Views

Handling User Interaction Events

To make your new widget interactive, it will need to respond to user events like key presses, screen touches, and button clicks. Android exposes several virtual event handlers, listed below, that let you react to user input:

❑onKeyDown Called when any device key is pressed; includes the D-pad, keyboard, hang-up, call, back, and camera buttons

❑onKeyUp Called when a user releases a pressed key

❑onTrackballEvent Called when the device’s trackball is moved

❑onTouchEvent Called when the touch screen is pressed or released, or it detects movement

The following code snippet shows a skeleton class that overrides each of the user interaction handlers in a View:

@Override

public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {

//Return true if the event was handled. return true;

}

@Override

public boolean onKeyUp(int keyCode, KeyEvent keyEvent) {

//Return true if the event was handled. return true;

}

@Override

public boolean onTrackballEvent(MotionEvent event ) {

Get the type of action this event represents int actionPerformed = event.getAction();

//Return true if the event was handled.

return true;

}

@Override

public boolean onTouchEvent(MotionEvent event) {

Get the type of action this event represents int actionPerformed = event.getAction();

//Return true if the event was handled.

return true;

}

Further details on using each of these event handlers, including greater detail on the parameters received by each method, are available in Chapter 11.

Creating a Compass View Example

In the following example, you’ll create a new Compass View by extending the View class. It uses a tradi-tional compass rose to indicate a heading/orientation. When complete, it should appear as in Figure 4-3.

A compass is an example of a User Interface control that requires a radically different visual display from the textboxes and buttons available in the SDK toolbox, making it an excellent candidate for building from scratch.

In Chapter 10, you’ll use this Compass View and the device’s built-in accelerometer to display the user’s current bearing. Then in Chapter 11, you will learn some advanced techniques for Canvas draw-ing that will let you dramatically improve its appearance.

Create a new Compass project that will contain your new Compass View, and an Activity to hold it. Now create a new CompassView class that extends View. Create constructors that will allow the View to be instantiated 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);

}

}

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;

}

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>

Now return to the CompassView class. Add a new property for the bearing to display and create get and set methods for it.

private float bearing;

public void setBearing(float _bearing) {

bearing = _bearing;

}

public float getBearing() {

return bearing;

}

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.color. background_color); circlePaint.setStrokeWidth(1); 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));

}

The final step is drawing the compass face using the Strings and Paint objects you created in Step 5. The following code snippet is presented with only limited commentary. You can find more detail on how to draw on the Canvas and use advanced Paint effects in Chapter 11.

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 ;

int radius = Math.min(px, py);

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 do 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);

Draw the cardinal points if (i % 6 == 0) {

String dirString = “”; switch (i) {

case(0)  : {

dirString = northString;

int arrowY = 2*textHeight;

canvas.drawLine(px, arrowY, px-5, 3*textHeight,

markerPaint);

canvas.drawLine(px, arrowY, px+5, 3*textHeight, markerPaint);

break;

}

case(6) : dirString = eastString; break;

case(12) : dirString = southString; break;

case(18) : dirString = westString; break;

}

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

}

else if (i % 3 == 0) {

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();

}

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>

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

Using Custom Controls

Having created you own custom Views, you can use them within code and layouts as you would any other Android control. The snippet below shows how to add the CompassView created in the above example to an Activity, by overriding the onCreate method:

@Override

public void onCreate(Bundle icicle) {
super.onCreate(icicle);

CompassView cv = new CompassView(this);

setContentView(cv);

cv.setBearing(45);

}

To use the same control within a layout resource, specify the fully qualified class name when you create a new node in the layout definition, as shown in the following XML snippet:

<com.paad.compass.CompassView

android:id=”@+id/compassView”

android:layout_width=”fill_parent”

android:layout_height=”fill_parent”

/>

You can inflate the layout and get a reference to the CompassView as normal, using the following code:

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.main);

CompassView cv = (CompassView)this.findViewById(R.id.compassView); cv.setBearing(45);

}