MessageUI 封裝常用的 Alert 顯示邏輯,提供靜態方法供全程式呼叫:
package com.nokia.example.location.tourist.ui;
import javax.microedition.lcdui.*;
import com.nokia.example.location.tourist.TouristMIDlet;
public class MessageUI {
/** 裝置不支援 Location API */
public static void showApiNotSupported() {
Alert alert = new Alert("Information",
"Device do not support Location API", null, AlertType.INFO);
TouristMIDlet.getDisplay().setCurrent(alert);
}
/** 定位資料尚未可用 */
public static void showLocationDataNotAvailable() {
Alert alert = new Alert("Information",
"Location data is not yet available.", null, AlertType.INFO);
TouristMIDlet.getDisplay().setCurrent(alert);
}
/** 正在尋找定位資料(含進度指示器) */
public static void showLocationProviderState() {
Gauge indicator = new Gauge(null, false, 50, 1);
indicator.setValue(Gauge.CONTINUOUS_RUNNING);
Alert alert = new Alert("Information",
"Please wait, looking for location data....", null, AlertType.INFO);
alert.setIndicator(indicator);
TouristMIDlet.getDisplay().setCurrent(alert);
}
}
PitchRollUI 同樣繼承 Canvas 並實作 Runnable,專門將 Orientation 的 pitch(俯仰角 ±90°)與 roll(滾轉角 ±180°)以類儀表板方式視覺化。
public class PitchRollUI extends Canvas implements CommandListener, Runnable {
private CompassUI compass;
private float pitch = Float.NaN, roll = Float.NaN;
private boolean threadActive = false;
private final long SLEEPTIME = 1000;
private Command compassCmd = new Command("Compass", Command.BACK, 1);
public PitchRollUI(CompassUI compass) {
this.compass = compass;
addCommand(compassCmd);
setCommandListener(this);
}
// ...
protected void showNotify() {
threadActive = true;
new Thread(this).start();
}
protected void hideNotify() {
threadActive = false;
}
public void run() {
while (threadActive) {
Orientation orientation = ConfigurationProvider.getInstance().getOrientation();
if (orientation != null) {
pitch = orientation.getPitch();
roll = orientation.getRoll();
}
repaint();
try { Thread.sleep(SLEEPTIME); }
catch (InterruptedException e) {}
}
}
protected void paint(Graphics g) {
g.setColor(255, 255, 255);
g.fillRect(0, 0, getWidth(), getHeight());
paintPitch(g);
paintRoll(g);
}
drawMeter 依參數繪製基線、小刻度、大刻度:
protected void drawMeter(Graphics g, int min, int max, int smallStep,
int largeStep, int baseline, int margin) {
double scale = 100.0;
g.setColor(0x0000ff);
g.drawLine(margin, baseline, getWidth(), baseline);
for (int i = min; i <= max; i += smallStep) {
double pos = (((i + max) * scale * (getWidth() - margin)) / (2 * max)) / scale;
int x = margin + (int) pos;
g.drawLine(x, baseline - 3, x, baseline + 3);
}
for (int i = min; i <= max; i += largeStep) {
double pos = (((i + max) * scale * (getWidth() - margin)) / (2 * max)) / scale;
int x = margin + (int) pos;
g.drawLine(x, baseline - 5, x, baseline + 5);
}
}
protected void paintPitch(Graphics g) {
int baseline = 10, textAreaWidth = 50;
g.setColor(0x000000);
g.drawString("Pitch", 0, baseline - 5, Graphics.TOP | Graphics.LEFT);
g.drawString("-90", textAreaWidth + 1, baseline, Graphics.TOP | Graphics.LEFT);
g.drawString("0", textAreaWidth + (getWidth() - textAreaWidth) / 2, baseline, Graphics.TOP | Graphics.HCENTER);
g.drawString("+90", getWidth(), baseline, Graphics.TOP | Graphics.RIGHT);
drawMeter(g, -90, 90, 10, 30, baseline, textAreaWidth);
if (pitch != Float.NaN) {
double pos = (((pitch + 90.0) * 100 * (getWidth() - textAreaWidth)) / (2 * 90)) / 100;
g.setColor(0x000000);
g.fillRect(textAreaWidth + (int) pos - 3, baseline - 2, 5, 5);
}
}
protected void paintRoll(Graphics g) {
int baseline = 40, textAreaWidth = 50;
g.setColor(0x000000);
g.drawString("Roll", 0, baseline - 5, Graphics.TOP | Graphics.LEFT);
g.drawString("-180", textAreaWidth + 1, baseline, Graphics.TOP | Graphics.LEFT);
g.drawString("0", textAreaWidth + (getWidth() - textAreaWidth) / 2, baseline, Graphics.TOP | Graphics.HCENTER);
g.drawString("+180", getWidth(), baseline, Graphics.TOP | Graphics.RIGHT);
drawMeter(g, -180, 180, 15, 60, baseline, textAreaWidth);
if (roll != Float.NaN) {
double pos = (((roll + 180.0) * 100 * (getWidth() - textAreaWidth)) / (2 * 180)) / 100;
g.setColor(0x000000);
g.fillRect(textAreaWidth + (int) pos - 3, baseline - 2, 5, 5);
}
}
public void commandAction(Command command, Displayable d) {
if (command == compassCmd) {
TouristMIDlet.getDisplay().setCurrent(compass);
}
}
}
總結: 本系列完整展示了 J2ME Location API (JSR-179) 在行動裝置上的實戰應用,涵蓋地標管理、定位監聽、鄰近偵測、電子羅盤與姿態感測器視覺化,為 LBS 應用開發提供了寶貴的參考架構。