📄 gmapcanvas.java
字号:
package org.sreid.j2me.gmapviewer;import java.util.*;import javax.microedition.lcdui.*;import javax.microedition.midlet.*;import org.sreid.j2me.util.*;class GMapCanvas extends Canvas implements CommandListener { private static final String MAIN_MENU = "Back"; private static final String ZOOM_IN = "Zoom in"; private static final String ZOOM_OUT = "Zoom out"; private static final String CREATE_MAP_PIN = "New map pin"; private static final String MAP_PIN_MENU = "Map pin menu"; private static final String SEARCH_MENU = "Search menu"; private final GMapViewer app; // Font for map pin text private final Font font = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL); // Absolute position. private int x, y; // Zoom level. 0-14. Smaller is more zoomed in. private int zoom; private long lastMoveTime = 0; int requestID = 0; // used by MapTileManager.getMapTile GMapCanvas(GMapViewer app) { this.app = app; setCommandListener(this); addCommand(new Command(MAIN_MENU, Command.BACK, 1)); addCommand(new Command(ZOOM_IN, Command.SCREEN, 1)); addCommand(new Command(ZOOM_OUT, Command.SCREEN, 1)); addCommand(new Command(CREATE_MAP_PIN, Command.SCREEN, 2)); addCommand(new Command(MAP_PIN_MENU, Command.SCREEN, 2)); addCommand(new Command(SEARCH_MENU, Command.SCREEN, 2)); } void setXY(int x, int y) { this.x = x; this.y = y; app.mpm.sortPinsNearestFirst(); repaint(); } int getX() { return x; } int getY() { return y; } int getZ() { return zoom; } // Move view position (relative, 0 = no change) private void moveView(int movx, int movy) { long t = System.currentTimeMillis(); boolean fast = (t < (lastMoveTime + app.prefs.getInt("moveSpeedTimeout", 800))); int speed = (fast ? app.prefs.getInt("moveSpeed2", 20) : app.prefs.getInt("moveSpeed1", 8)); movx *= speed; movy *= speed; setXY(x + (movx << zoom), y + (movy << zoom)); lastMoveTime = t; } private void zoomIn() { if (zoom > 0) zoom--; repaint(); } private void zoomOut() { if (zoom < 14) zoom++; repaint(); } public void paint(Graphics g) { // Convert x,y to screen coordinates final int w = getWidth(); final int h = getHeight(); final int sx = (x >> zoom) - (w / 2); // screen x position final int sy = (y >> zoom) - (h / 2); // screen y position final int xpos = sx >> 7; final int xoff = sx & 0x7f; final int ypos = sy >> 7; final int yoff = sy & 0x7f; //if (app.debug) app.debug("Position: " + x + " " + y + " -> " + xpos + " " + xoff + " " + ypos + " " + yoff + " " + zoom); // Number of tiles wide/high. Round up, and add 1 to handle overlap. final int tw = ((w + MapTile.WIDTH - 1) / MapTile.WIDTH) + 1; final int th = ((h + MapTile.HEIGHT - 1) / MapTile.HEIGHT) + 1; // Make sure image cache size is large enough that we don't // have to re-decode tiles on every refresh. app.cache.imageCacheSize = tw * th + 1; // Identify visible tiles XYZ[] visible = new XYZ[tw * th]; int nVisible = 0; for (int ty = 0; ty < th; ty++) { for (int tx = 0; tx < tw; tx++) { XYZ xyz = new XYZ(xpos + tx, ypos + ty, zoom); if (computeTileVisiblePixels(xyz, sx, sy, w, h) > 0) { visible[nVisible++] = xyz; } } } // Sort tiles by how much space in the on-screen area it affects, // so that we will request the most visible tiles first. Sort.sort(visible, 0, nVisible, new Comparator() { public int compare(Object o1, Object o2) { int v1, v2; // First, compare by visible pixels v1 = computeTileVisiblePixels((XYZ)o1, sx, sy, w, h); v2 = computeTileVisiblePixels((XYZ)o2, sx, sy, w, h); if (v1 != v2) return v2 - v1; // If visible pixels the same (probably fully visible), then compare by distance from center v1 = computeTileDistance((XYZ)o1, sx, sy, w, h); v2 = computeTileDistance((XYZ)o2, sx, sy, w, h); return v1 - v2; } }); // Request/draw the tiles requestID++; for (int i = 0; i < nVisible; i++) { XYZ xyz = visible[i]; MapTile mt = app.mtm.getMapTile(xyz); int tx = (xyz.x * MapTile.WIDTH) - ((xpos * MapTile.WIDTH) + xoff); int ty = (xyz.y * MapTile.HEIGHT) - ((ypos * MapTile.HEIGHT) + yoff); Image image = mt.image; // take a reference now, in case background thread nulls out the field if (image != null) { // Got the image. Draw it. g.drawImage(image, tx, ty, Graphics.TOP|Graphics.LEFT); } else if (mt.bytes == null && mt.rmsID == -1) { if (mt.downloading) { // yellow to indicate downloading drawEmptyTile(g, 0xffff00, tx, ty); } else { // orange to indicate waiting for download drawEmptyTile(g, 0xff8000, tx, ty); } } else { // Image is null, but does not need to be downloaded. That means we're waiting for decode. if (mt.decoding) { // green to indicate decoding drawEmptyTile(g, 0x00ff00, tx, ty); } else { // blue to indicate waiting for decode drawEmptyTile(g, 0x0000ff, tx, ty); } } } // Draw the map pins over top of the tiles. g.setFont(font); final int fudge = 100; final int PW = 16, PH = 16; // pin size final long mapPinTextRadius = app.prefs.getInt("mapPinTextRadius", 600); final long mapPinTextRadiusSquared = mapPinTextRadius * mapPinTextRadius; for (int i = app.mpm.getMapPinCount() - 1; i >= 0; i--) { // reverse order, so closer map pins will overwrite more distant ones MapPin pin = app.mpm.getMapPin(i); if (pin == null) continue; int px = (pin.x >> zoom) - sx; int py = (pin.y >> zoom) - sy; if (px >= (-fudge) && px <= (w + fudge) && py >= (-fudge) && py <= (h + fudge)) { // The pin is on-screen, or close enough. Draw it. g.setColor(pin.isPermanent ? 0xff0000 : 0x0000ff); g.fillArc(px - PW, py - PH, PW*2, PH*2, 90, 15); g.drawLine(px, py - PH, px, py); // draw a line too, so it always goes right to the point. // Draw text? (pythagorean theorem) int hdist = Math.abs(x - pin.x); int vdist = Math.abs(y - pin.y); if ( (hdist * hdist) + (vdist * vdist) < mapPinTextRadiusSquared) { g.setColor(0xffffff); g.fillRect(px + 1, py - PH, font.stringWidth(pin.text), font.getHeight()); g.setColor(0x000000); g.drawString(pin.text, px + 1, py - PH, Graphics.TOP|Graphics.LEFT); } } } // Draw crosshairs g.setColor(0x808080); final int cs = 3; // crosshair size g.drawLine(w/2 - cs, h/2, w/2 + cs, h/2); g.drawLine(w/2, h/2 - cs, w/2, h/2 + cs); if (app.prefs.getInt("callgc", 0) >= 3) System.gc(); } // Returns the number of pixels currently visible from the specified tile. // Return value ranges from 0 (completely off-screen) to // MapTile.WIDTH*MapTile.HEIGHT (completely on-screen). private int computeTileVisiblePixels(XYZ tile, int sx, int sy, int w, int h) { // convert to pixels int tx = tile.x * MapTile.WIDTH; int ty = tile.y * MapTile.HEIGHT; // figure visible width, visible height int vw = Math.max(0, ( Math.min(tx + MapTile.WIDTH, sx + w) - Math.max(tx, sx) )); int vh = Math.max(0, ( Math.min(ty + MapTile.HEIGHT, sy + h) - Math.max(ty, sy) )); // area return vw * vh; } // Returns the distance (squared actually) of the center of the specified // tile from the center of the screen. private int computeTileDistance(XYZ tile, int sx, int sy, int w, int h) { // convert to pixels int tx = tile.x * MapTile.WIDTH; int ty = tile.y * MapTile.HEIGHT; // Compute horizontal and vertical distances int hdist = Math.abs( (sx + (w / 2)) - (tx + (MapTile.WIDTH / 2)) ); int vdist = Math.abs( (sy + (h / 2)) - (ty + (MapTile.HEIGHT / 2)) ); // Pythagorean theorem return (hdist * hdist) + (vdist * vdist); // don't need longs here, as distance will always be small } private void drawEmptyTile(Graphics g, int color, int x, int y) { // Solid color g.setColor(color); g.fillRect(x, y, MapTile.WIDTH, MapTile.HEIGHT); // ...with a thin black border g.setColor(0x000000); g.drawRect(x, y, MapTile.WIDTH, MapTile.HEIGHT); } protected void keyPressed(int keyCode) { doKey(keyCode); } protected void keyRepeated(int keyCode) { doKey(keyCode); } private void doKey(int keyCode) { //app.debug("Key pressed: " + keyCode + "/" + getGameAction(keyCode) + "/" + getKeyName(keyCode)); switch (getGameAction(keyCode)) { case UP: moveView(0, -1); break; case DOWN: moveView(0, 1); break; case LEFT: moveView(-1, 0); break; case RIGHT: moveView(1, 0); break; } } public void commandAction(Command c, Displayable d) { String cmd = c.getLabel(); if (cmd.equals(ZOOM_IN)) { zoomIn(); } if (cmd.equals(ZOOM_OUT)) { zoomOut(); } if (cmd.equals(CREATE_MAP_PIN)) { createMapPin(); } if (cmd.equals(MAP_PIN_MENU)) { app.mapPinMenu.cameFrom = this; app.mapPinMenu.loadList(); app.display.setCurrent(app.mapPinMenu); } if (cmd.equals(SEARCH_MENU)) { app.searchMenu.cameFrom = this; app.searchMenu.loadList(); app.display.setCurrent(app.searchMenu); } if (cmd.equals(MAIN_MENU)) { app.display.setCurrent(app.mainMenu); } } private void createMapPin() { final Dialog dlg = Dialog.createTextDialog("New map pin", "Map pin text", "", 40, TextField.ANY); dlg.setCallback(new Runnable() { public void run() { String userResponse = (String)dlg.getUserResponse(); if (userResponse != null) { MapPin pin = new MapPin(x, y); pin.text = (String)userResponse; app.mpm.keepPin(pin); } }}); dlg.showOn(app); } /** Loads position from preferences. */ void loadPosition() { x = app.prefs.getInt("canvas.x", -2505728); y = app.prefs.getInt("canvas.y", -1282048); zoom = app.prefs.getInt("canvas.zoom", 6); } /** Saves position to preferences. */ void savePosition() { app.prefs.put("canvas.x", Integer.toString(x)); app.prefs.put("canvas.y", Integer.toString(y)); app.prefs.put("canvas.zoom", Integer.toString(zoom)); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -