首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> J2ME

J2ME 自定義元件開發:可複用、跨 Canvas/Form 的計數器組件實作

     編輯:J2ME

設計目標

打造一個同時支援 Canvas 繪製與 MIDP 2.0 CustomItem的計數器組件,核心需求:

  • 自動遞增計數 (可設定間隔)。
  • 視覺自適應:文字過大自動擴展繪製區域。
  • 零 GC 壓力:複用字串、字型、Graphics 物件。
  • 雙模式整合:Canvas 直接呼叫 paint();Form 透過 CustomItem 封裝。

核心邏輯層:CounterArea (純邏輯、無 UI 相依)


public class CounterArea {
    public static final int DEFAULT_RATE = 500; // ms
    public static final int MIN_RATE = 100;

    // 回呼介面:將無效化、重繪、尺寸變更委派給外層控制器 (Canvas/CustomItem)
    public interface Callback {
        void invalidateCounter(CounterArea counter);
        void repaintCounter(CounterArea counter);
        void resizeCounter(CounterArea counter);
    }

    private int value = 0;
    private int rate = DEFAULT_RATE;
    private int left, top, width = -1, height = -1;
    private int backColor = 0xFFFFFF, textColor = 0x000000;
    private Font font;
    private Callback callback;
    private Thread timerThread;
    private volatile boolean running = false;

    // --- 屬性存取 (含自動無效化) ---
    public void setBackColor(int c) { backColor = c; invalidate(); }
    public void setTextColor(int c) { textColor = c; invalidate(); }
    public void setFont(Font f) { font = f; invalidate(); }
    public void setRate(int r) { rate = Math.max(r, MIN_RATE); }
    public void setLeft(int l) { left = l; }
    public void setTop(int t) { top = t; }
    public void setWidth(int w) { width = w; invalidate(); }
    public void setHeight(int h) { height = h; invalidate(); }
    public synchronized void setValue(int v) { value = v; repaint(); }

    public int getValue() { return value; }
    public int getWidth() { return width < 0 ? getMinWidth() : width; }
    public int getHeight() { return height < 0 ? getMinHeight() : height; }
    public int getMinWidth() {
        Font f = font != null ? font : Font.getDefaultFont();
        return f.stringWidth(String.valueOf(value));
    }
    public int getMinHeight() {
        Font f = font != null ? font : Font.getDefaultFont();
        return f.getHeight();
    }

    // --- 繪製核心:右對齊、垂直置中 ---
    public void paint(Graphics g) {
        String s = String.valueOf(value);
        Font f = font != null ? font : Font.getDefaultFont();
        int tw = f.stringWidth(s);
        int th = f.getHeight();
        int w = getWidth(), h = getHeight();

        g.setColor(backColor);
        g.fillRect(left, top, w, h);
        g.setColor(textColor);
        g.drawString(s, left + w - tw, top + (h - th) / 2, Graphics.TOP | Graphics.LEFT);

        // 自適應擴展
        if (tw > w || th > h) resize();
    }

    // --- 計時器執行緒 ---
    public synchronized void start() {
        if (running) return;
        running = true;
        timerThread = new Thread(() -> {
            while (running) {
                try { Thread.sleep(rate); } catch (InterruptedException ignored) {}
                synchronized (CounterArea.this) {
                    if (!running) break;
                    value++;
                    repaint();
                }
            }
        }, "CounterTimer");
        timerThread.start();
    }

    public synchronized void stop() {
        running = false;
        timerThread = null;
    }

    public boolean isCounting() { return running; }

    // --- 回呼委派 ---
    private void invalidate() { if (callback != null) callback.invalidateCounter(this); }
    private void repaint() { if (callback != null) callback.repaintCounter(this); }
    private void resize() { if (callback != null) callback.resizeCounter(this); }

    public void setCallback(Callback cb) { this.callback = cb; }
}

模式一:直接在 Canvas 使用


public class CounterCanvas extends Canvas {
    private CounterArea counter;

    public CounterCanvas() {
        counter = new CounterArea(10, 10); // left=10, top=10
        counter.setCallback(new CounterArea.Callback() {
            public void invalidateCounter(CounterArea c) { repaint(); }
            public void repaintCounter(CounterArea c) { repaint(c.getLeft(), c.getTop(), c.getWidth(), c.getHeight()); }
            public void resizeCounter(CounterArea c) { repaint(); } // 簡單全畫面重繪
        });
        counter.setRate(1000); // 1 秒遞增
        counter.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE));
        counter.setTextColor(0x0000FF);
    }

    protected void paint(Graphics g) {
        g.setColor(0xFFFFFF); g.fillRect(0,0,getWidth(),getHeight());
        counter.paint(g);
    }

    protected void showNotify() { counter.start(); }
    protected void hideNotify() { counter.stop(); }
}

模式二:封裝為 MIDP 2.0 CustomItem (可放入 Form)


public class CounterItem extends CustomItem implements CounterArea.Callback {
    private CounterArea area;

    public CounterItem() {
        super(null);
        area = new CounterArea();
        area.setCallback(this);
    }

    // CustomItem 必須實作:最小/偏好尺寸
    public int getMinContentWidth() { return area.getMinWidth(); }
    public int getMinContentHeight() { return area.getMinHeight(); }
    public int getPrefContentWidth(int h) { return getMinContentWidth(); }
    public int getPrefContentHeight(int w) { return getMinContentHeight(); }

    // 佈局變更時同步尺寸
    protected void sizeChanged(int w, int h) {
        area.setWidth(w);
        area.setHeight(h);
    }

    // 顯示/隱藏控制計時器
    protected void showNotify() { area.start(); }
    protected void hideNotify() { area.stop(); }

    // 繪製委派
    protected void paint(Graphics g, int w, int h) {
        area.setWidth(w);
        area.setHeight(h);
        area.paint(g);
    }

    // Callback 實作:轉發給 CustomItem 系統
    public void invalidateCounter(CounterArea c) { invalidate(); }
    public void repaintCounter(CounterArea c) { repaint(); }
    public void resizeCounter(CounterArea c) { invalidate(); }

    // 對外便利方法
    public void setValue(int v) { area.setValue(v); }
    public int getValue() { return area.getValue(); }
    public void setFont(Font f) { area.setFont(f); }
    public void setRate(int r) { area.setRate(r); }
    public void setColors(int bg, int fg) { area.setBackColor(bg); area.setTextColor(fg); }

    // 不支援焦點遍歷
    public boolean traverse(int dir, int vw, int vh, int[] vrect) { return false; }
}

測試 MIDlet:雙模式並行展示


public class CounterDemoMIDlet extends MIDlet implements CommandListener {
    private Display display;
    private Command exitCmd = new Command("Exit", Command.EXIT, 1);
    private Command switchCmd = new Command("Switch", Command.SCREEN, 2);
    private boolean showCanvas = true;

    protected void startApp() {
        if (display == null) {
            display = Display.getDisplay(this);
            showCanvasView();
        }
    }

    private void showCanvasView() {
        CounterCanvas canvas = new CounterCanvas();
        canvas.addCommand(exitCmd);
        canvas.addCommand(switchCmd);
        canvas.setCommandListener(this);
        display.setCurrent(canvas);
        showCanvas = true;
    }

    private void showFormView() {
        Form form = new Form("CounterItem Demo");
        CounterItem item = new CounterItem();
        item.setLayout(Item.LAYOUT_CENTER | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER);
        item.setFont(Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_ITALIC, Font.SIZE_MEDIUM));
        item.setColors(0xFFFF00, 0xFF0000); // 黃底紅字
        form.append(item);
        form.addCommand(exitCmd);
        form.addCommand(switchCmd);
        form.setCommandListener(this);
        display.setCurrent(form);
        showCanvas = false;
    }

    public void commandAction(Command c, Displayable d) {
        if (c == exitCmd) notifyDestroyed();
        else if (c == switchCmd) {
            if (showCanvas) showFormView(); else showCanvasView();
        }
    }
    protected void pauseApp() {}
    protected void destroyApp(boolean u) {}
}

架構優勢總結

特性傳統寫法CounterArea 方案
程式碼複用Canvas/Form 寫兩套核心邏輯單一來源
記憶體效率每幀新建 String/Font複用緩衝區、字型、回呼
擴充性修改分散擴充 CounterArea 即雙模受益
測試性需 UI 環境純邏輯可單元測試
生命週期管理手動控制 ThreadshowNotify/hideNotify 自動綁定

進階應用場景

  • 遊戲計分板:整合分數、連擊數、倒數計時多個 CounterArea。
  • 下載進度條:將 value 映射為進度百分比,paint() 繪製長條而非文字。
  • 系統監控:顯示 FPS、記憶體使用量、網路延遲等即時指標。

小結

「邏輯與 UI 分離、回呼介面雙向綁定、Canvas 與 CustomItem 共享核心類」是 J2ME 自定義元件開發的黃金模式。CounterArea 以不足 150 行代碼展示了此模式的精髓:高內聚、低耦合、零浪費。建議開發者將此模式內化為元件庫基因,加速後續 UI 開發。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved