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

Java AWT 簡易記事本完整實作:菜單、文件讀寫、剪貼簿、事件處理全流程

     編輯:Java 相關知識

雖然現代 Java 桌面開發主流已轉向 JavaFX/Swing,但 java.awt 作為基礎圖形庫,理解其**事件委派模型、組件層次、原生對等體機制**對掌握 Java GUI 本質極有幫助。本文提供一個功能完整的 AWT 記事本,涵蓋菜單欄、文件開啟/儲存、全選/複製/剪貼/貼上、視窗關閉等核心功能。

完整原始碼 (單檔案可直接運行)

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.*;

public class SimpleNotepad extends Frame implements ActionListener {
    private static final long serialVersionUID = 1L;

    // UI 組件
    private final TextArea textArea = new TextArea();
    private final MenuBar menuBar = new MenuBar();
    private final Menu fileMenu = new Menu("File");
    private final Menu editMenu = new Menu("Edit");

    // File 菜單項
    private final MenuItem newItem = new MenuItem("New");
    private final MenuItem openItem = new MenuItem("Open");
    private final MenuItem saveItem = new MenuItem("Save");
    private final MenuItem saveAsItem = new MenuItem("Save As");
    private final MenuItem exitItem = new MenuItem("Exit");

    // Edit 菜單項
    private final MenuItem selectAllItem = new MenuItem("Select All");
    private final MenuItem copyItem = new MenuItem("Copy");
    private final MenuItem cutItem = new MenuItem("Cut");
    private final MenuItem pasteItem = new MenuItem("Paste");

    // 狀態
    private String fileName = null;
    private final Toolkit toolkit = Toolkit.getDefaultToolkit();
    private final Clipboard clipboard = toolkit.getSystemClipboard();

    // 文件對話框 (AWT 原生)
    private final FileDialog openDialog = new FileDialog(this, "Open File", FileDialog.LOAD);
    private final FileDialog saveDialog = new FileDialog(this, "Save File As", FileDialog.SAVE);

    public SimpleNotepad() {
        initUI();
        initEvents();
    }

    private void initUI() {
        setTitle("簡易記事本 - AWT 版");
        setFont(new Font("Monospaced", Font.PLAIN, 14));
        setBackground(Color.WHITE);
        setSize(600, 450);
        setLocationRelativeTo(null); // 居中 (需 JDK 1.4+)

        // 構建菜單
        fileMenu.add(newItem);
        fileMenu.add(openItem);
        fileMenu.addSeparator();
        fileMenu.add(saveItem);
        fileMenu.add(saveAsItem);
        fileMenu.addSeparator();
        fileMenu.add(exitItem);

        editMenu.add(selectAllItem);
        editMenu.addSeparator();
        editMenu.add(copyItem);
        editMenu.add(cutItem);
        editMenu.add(pasteItem);

        menuBar.add(fileMenu);
        menuBar.add(editMenu);
        setMenuBar(menuBar);

        add(textArea, BorderLayout.CENTER); // Frame 默認 BorderLayout
    }

    private void initEvents() {
        // 視窗關閉
        addWindowListener(new WindowAdapter() {
            @Override public void windowClosing(WindowEvent e) {
                exitApplication();
            }
        });

        // 統一事件監聽
        MenuItem[] items = {newItem, openItem, saveItem, saveAsItem, exitItem,
                            selectAllItem, copyItem, cutItem, pasteItem};
        for (MenuItem item : items) {
            item.addActionListener(this);
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        Object src = e.getSource();

        if (src == newItem) {
            newFile();
        } else if (src == openItem) {
            openFile();
        } else if (src == saveItem) {
            saveFile();
        } else if (src == saveAsItem) {
            saveAsFile();
        } else if (src == exitItem) {
            exitApplication();
        } else if (src == selectAllItem) {
            textArea.selectAll();
        } else if (src == copyItem) {
            copyToClipboard();
        } else if (src == cutItem) {
            cutToClipboard();
        } else if (src == pasteItem) {
            pasteFromClipboard();
        }
    }

    // === 文件操作 ===
    private void newFile() {
        textArea.setText("");
        fileName = null;
        setTitle("簡易記事本 - AWT 版");
    }

    private void openFile() {
        openDialog.setVisible(true); // 阻塞直到用戶選擇
        String dir = openDialog.getDirectory();
        String file = openDialog.getFile();
        if (dir != null && file != null) {
            fileName = dir + file;
            readFile(fileName);
            setTitle("簡易記事本 - " + file);
        }
    }

    private void saveFile() {
        if (fileName != null) {
            writeFile(fileName);
        } else {
            saveAsFile();
        }
    }

    private void saveAsFile() {
        saveDialog.setVisible(true);
        String dir = saveDialog.getDirectory();
        String file = saveDialog.getFile();
        if (dir != null && file != null) {
            fileName = dir + file;
            writeFile(fileName);
            setTitle("簡易記事本 - " + file);
        }
    }

    private void readFile(String path) {
        try (FileReader reader = new FileReader(path)) {
            char[] buffer = new char[8192];
            StringBuilder content = new StringBuilder();
            int len;
            while ((len = reader.read(buffer)) != -1) {
                content.append(buffer, 0, len);
            }
            textArea.setText(content.toString());
        } catch (IOException ex) {
            showError("讀取文件失敗: " + ex.getMessage());
        }
    }

    private void writeFile(String path) {
        try (FileWriter writer = new FileWriter(path)) {
            writer.write(textArea.getText());
        } catch (IOException ex) {
            showError("儲存文件失敗: " + ex.getMessage());
        }
    }

    // === 剪貼簿操作 ===
    private void copyToClipboard() {
        String selected = textArea.getSelectedText();
        if (selected != null && !selected.isEmpty()) {
            StringSelection selection = new StringSelection(selected);
            clipboard.setContents(selection, null);
        }
    }

    private void cutToClipboard() {
        copyToClipboard();
        textArea.replaceRange("", textArea.getSelectionStart(), textArea.getSelectionEnd());
    }

    private void pasteFromClipboard() {
        Transferable contents = clipboard.getContents(this);
        if (contents != null && contents.isDataFlavorSupported(DataFlavor.stringFlavor)) {
            try {
                String text = (String) contents.getTransferData(DataFlavor.stringFlavor);
                textArea.replaceRange(text, textArea.getSelectionStart(), textArea.getSelectionEnd());
            } catch (Exception ex) {
                showError("貼上失敗: " + ex.getMessage());
            }
        }
    }

    private void exitApplication() {
        System.exit(0);
    }

    private void showError(String msg) {
        // AWT 無標準對話框,簡單輸出到控制台
        System.err.println(msg);
        // 實際專案可用 new Dialog(this, "錯誤", true) 自定義錯誤彈窗
    }

    public static void main(String[] args) {
        // AWT 事件分發線程啟動 (可選但推薦)
        EventQueue.invokeLater(() -> {
            SimpleNotepad notepad = new SimpleNotepad();
            notepad.setVisible(true);
        });
    }
}

核心技術點解析

1. AWT 事件委派模型

  • ActionListener + actionPerformed 處理菜單/按鈕點擊。
  • WindowAdapter 適配器模式簡化 WindowListener (僅覆寫 windowClosing)。
  • 所有 UI 事件在 **Event Dispatch Thread (EDT)** 執行,主程式用 EventQueue.invokeLater 啟動。

2. 原生對等體

  • AWT 組件 (Frame, Button, TextArea, Menu, FileDialog) 底層調用**作業系統原生 GUI API** (Windows: USER32/GDI, Linux: X11/Motif, macOS: Carbon/Cocoa)。
  • 優點:啟動快、記憶體佔用低、原生外觀。缺點:跨平台一致性差、組件豐富度低 (無表格、樹、進度條等)。

3. FileDialog 模態阻塞

openDialog.setVisible(true); // 當前線程阻塞,直到用戶關閉對話框
String file = openDialog.getFile(); // 獲取選擇結果

這是 AWT 簡單同步 API 設計,現代 Swing/JavaFX 採用異步回調模式。

4. 剪貼簿數據傳輸

  • Toolkit.getDefaultToolkit().getSystemClipboard() 獲取系統剪貼簿。
  • StringSelection 實現 Transferable,封裝純文本。
  • DataFlavor.stringFlavor 標識 MIME 類型 text/plain; charset=utf-16。

5. TextArea 基礎編輯操作

  • getSelectedText() / getSelectionStart() / getSelectionEnd() 獲取選區。
  • replaceRange(str, start, end) 替換指定範圍文本 (實現剪切/貼上)。
  • selectAll() 全選。

從 AWT 到 Swing/JavaFX 的演進對比

特性AWT (本例)SwingJavaFX
組件豐富度基礎 (無表格/樹)豐富 (JTable, JTree, JTabbedPane...)極豐富 (TableView, TreeView, WebView, 3D...)
外觀一致性原生 (平台差異大)可換膚 (Metal, Nimbus, 系統風格)CSS 樣式化、跨平台高度一致
繪圖管線Graphics (即時模式)Graphics2D (保留模式、雙緩衝)Prism (GPU 加速、場景圖)
事件模型委派模型 (1.1+)同 AWT,擴展 EventListenerList觀察者模式 + 屬性綁定
線程模型單一 EDT單一 EDT + SwingWorkerJavaFX Application Thread + Task/Service
現代狀態維護模式 (JDK 內建)維護模式 (JDK 內建,Java 9+ 模組化)**主流推薦** (OpenJFX 獨立發布)

練習擴展建議

  1. 添加「撤銷/重做」:用 UndoManager (Swing) 或自建命令棧。
  2. 添加「查找/替換」:TextArea.getText() 正則搜尋,高亮需自繪或遷移 Swing JTextArea + Highlighter。
  3. 行號顯示:左側添加同步滾動的 TextArea 或 Canvas 繪製行號。
  4. 語法高亮:遷移 Swing JTextPane + StyledDocument 或 JavaFX CodeArea (RichTextFX)。
  5. 多標籤頁:CardLayout (AWT) 或 JTabbedPane (Swing)。

這個 200 行左右的 AWT 記事本,雖然功能簡單,卻完整展示了 Java GUI 程式設計的**骨架**:視窗容器、佈局管理、菜單系統、事件迴路、原生對話框、文件 I/O、系統剪貼簿。理解它,就是理解 Java 桌面開發的根基。

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