CompassUI 繼承 Canvas,實作 CommandListener 與 Runnable,負責:
Orientation 感測器取得方位角package com.nokia.example.location.tourist.ui;
import java.io.IOException;
import javax.microedition.lcdui.*;
import javax.microedition.location.Orientation;
import com.nokia.example.location.tourist.TouristMIDlet;
import com.nokia.example.location.tourist.model.ConfigurationProvider;
public class CompassUI extends Canvas implements CommandListener, Runnable {
private final int X = 0, Y = 1;
private int centerX, centerY;
private final float degree = (float) (2 * Math.PI / 360.0);
private float azimuth = 0.0f;
private boolean isMagnetic;
private boolean threadActive = false;
private final long SLEEPTIME = 1000;
private Displayable route, pitchrollUI;
private Command routeCmd = new Command("Route", Command.BACK, 1);
private Command prCmd = new Command("Pitch and Roll", Command.OK, 1);
private Image compassImage = null;
// ...
public CompassUI(Displayable route) {
this.route = route;
pitchrollUI = new PitchRollUI(this);
loadCompassImage();
centerX = getWidth() / 2;
centerY = getHeight() / 2;
addCommand(routeCmd);
addCommand(prCmd);
setCommandListener(this);
}
private void loadCompassImage() {
String imageName = "/compass_small.gif";
if (getWidth() > 160) imageName = "/compass_large.gif";
try {
compassImage = Image.createImage(getClass().getResourceAsStream(imageName));
} catch (IOException e) {
e.printStackTrace();
}
}
protected void showNotify() {
threadActive = true;
new Thread(this).start();
}
protected void hideNotify() {
threadActive = false;
}
清除畫布、繪製背景圖、根據 azimuth 旋轉繪製指標、顯示方位類型(磁北/真北):
protected void paint(Graphics g) {
g.setColor(255, 255, 255);
g.fillRect(0, 0, getWidth(), getHeight());
int spikeLen = 5;
int len = (compassImage.getWidth() / 2) - spikeLen;
g.drawImage(compassImage,
(getWidth() - compassImage.getWidth()) / 2,
centerY, Graphics.LEFT | Graphics.VCENTER);
g.setColor(0, 0, 255);
drawArrow(g, degree * azimuth, len, spikeLen);
g.setColor(0, 0, 255);
String otext = isMagnetic ? "Magnetic Field" : "True North";
g.drawString("Orientation: " + otext, 0, getHeight(),
Graphics.BOTTOM | Graphics.LEFT);
}
使用 2x2 旋轉矩陣計算指標四個端點座標:
private void drawArrow(Graphics g, float angle, int len, int spikeLen) {
int a[] = rotate(angle, 0, -(len - spikeLen));
int b[] = rotate(angle, -spikeLen, -(len - spikeLen));
int c[] = rotate(angle, 0, -len);
int d[] = rotate(angle, spikeLen, -(len - spikeLen));
int e[] = rotate(angle + (degree * 180.0), 0, -len);
g.setColor(255, 0, 0); // 紅色指標
g.drawLine(centerX, centerY, centerX + a[X], centerY + a[Y]);
g.drawLine(centerX + b[X], centerY + b[Y], centerX + d[X], centerY + d[Y]);
g.drawLine(centerX + b[X], centerY + b[Y], centerX + c[X], centerY + c[Y]);
g.drawLine(centerX + c[X], centerY + c[Y], centerX + d[X], centerY + d[Y]);
g.setColor(0, 0, 0); // 黑色反向指標
g.drawLine(centerX, centerY, centerX + e[X], centerY + e[Y]);
}
private int[] rotate(double angle, int x, int y) {
int rotated[] = new int[2];
rotated[X] = (int) (Math.cos(angle) * x + Math.sin(angle) * y);
rotated[Y] = (int) (-Math.sin(angle) * x + Math.cos(angle) * y);
return rotated;
}
public void run() {
while (threadActive) {
Orientation orientation = ConfigurationProvider.getInstance().getOrientation();
if (orientation != null) {
isMagnetic = orientation.isOrientationMagnetic();
azimuth = orientation.getCompassAzimuth();
}
repaint();
try { Thread.sleep(SLEEPTIME); }
catch (InterruptedException e) {}
}
}
public void commandAction(Command command, Displayable d) {
if (command == routeCmd) {
TouristMIDlet.getDisplay().setCurrent(route);
} else if (command == prCmd) {
TouristMIDlet.getDisplay().setCurrent(pitchrollUI);
}
}
}
技術亮點: 採用雙緩衝繪製(Canvas 內建)、旋轉矩陣運算指標幾何、背景執行緒輪詢感測器,實現流暢的電子羅盤體驗。