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

Java 記事本程式 (二):事件處理與核心功能實作詳解

     編輯:Java 程式設計入門

概述

承接上篇 GUI 架構,本文聚焦於事件監聽器實作與核心功能邏輯:檔案操作 (開啟/儲存/另存為)、編輯操作 (剪貼簿/搜尋取代/全選/時間戳)、右鍵選單、視窗關閉確認等。代碼採用 ActionListener 適配器模式,將 UI 元件與業務邏輯解耦。

視窗關閉與文本區事件


// 視窗關閉:攔截關閉事件,提示儲存未存檔內容
public void frame_windowClosing(WindowEvent e) {
    this.close(); // 內部處理 dirty flag 與確認對話框
}

// 滑鼠右鍵彈出選單
public void text_mouseClicked(MouseEvent e) {
    if (SwingUtilities.isRightMouseButton(e)) {
        pop.show((Component) e.getSource(), e.getX(), e.getY());
    }
}

// 文本區新增/載入時重置 dirty 標記
public void text_ancestorAdded(AncestorEvent e) {
    this.dirty = false;
    this.newText();
}

// Caret 位置變更標記 dirty (內容已修改)
public void text_caretUpdate(CaretEvent e) {
    this.dirty = true;
}

檔案選單功能


public void File_open_actionPerformed(ActionEvent e)  { this.openText(); }
public void File_new_actionPerformed(ActionEvent e)   { this.newText(); }
public void File_save_actionPerformed(ActionEvent e)  { this.save(); }
public void File_saveas_actionPerformed(ActionEvent e){ this.saveAs(); }
public void File_quit_actionPerformed(ActionEvent e)  { this.close(); }

核心輔助方法實作要點

  • openText():JFileChooser 選檔 → Files.readString(path) (JDK 11+) 或 BufferedReader 讀入 → 設定 textArea.setText() → 更新 filename、dirty=false。
  • save():若 filename 為空則委派 saveAs();否則 Files.writeString(path, textArea.getText()) → dirty=false。
  • saveAs():JFileChooser.SAVE_DIALOG 選路徑 → 覆寫確認 → 呼叫 save()。
  • close():if (dirty) { int opt = JOptionPane.showConfirmDialog(...); if (opt==YES) save(); else if (opt==CANCEL) return; } System.exit(0);

編輯選單功能


// 剪貼簿操作:直接委派 JTextArea 內建動作
public void Edit_cut_actionPerformed(ActionEvent e)    { text.cut(); }
public void Edit_copy_actionPerformed(ActionEvent e)   { text.copy(); }
public void Edit_paste_actionPerformed(ActionEvent e)  { text.paste(); }
public void Edit_delete_actionPerformed(ActionEvent e) { text.replaceSelection(""); }

// 撤銷/重做:需搭配 UndoManager (見下節)
public void Edit_undo_actionPerformed(ActionEvent e)   { this.undo(); }

// 搜尋
public void Edit_find_actionPerformed(ActionEvent e) {
    FindDialog dlg = new FindDialog(frame, "搜尋", true);
    dlg.setVisible(true);
    if (dlg.isConfirmed()) {
        String kw = dlg.getKeyword().trim();
        if (dlg.isForward()) this.findNext(kw, text.getCaretPosition(), text.getText().length());
        else this.findPrevious(kw, text.getCaretPosition());
    }
}

// 取代
public void Edit_replace_actionPerformed(ActionEvent e) {
    ReplaceDialog dlg = new ReplaceDialog(frame, "取代", true);
    dlg.setVisible(true);
    if (dlg.isConfirmed()) {
        this.replaceStr(dlg.getFindStr().trim(), dlg.getReplaceStr().trim(),
                        text.getCaretPosition(), text.getText().length());
    }
}

// 全選
public void Edit_selectall_actionPerformed(ActionEvent e) {
    text.setSelectionStart(0);
    text.setSelectionEnd(text.getText().length());
}

// 插入時間戳
public void Edit_timedate_actionPerformed(ActionEvent e) {
    String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    text.append(" " + now);
}

撤銷/重做機制:UndoManager 整合


private final UndoManager undoManager = new UndoManager();

{
    // 文本區 Document 掛載 UndoableEditListener
    text.getDocument().addUndoableEditListener(e -> undoManager.addEdit(e.getEdit()));
}

// 選單項目啟用狀態動態更新
private void updateUndoRedoState() {
    undoMenuItem.setEnabled(undoManager.canUndo());
    redoMenuItem.setEnabled(undoManager.canRedo());
    undoMenuItem.setText(undoManager.canUndo() ? "撤銷 (" + undoManager.getUndoPresentationName() + ")" : "撤銷");
    redoMenuItem.setText(undoManager.canRedo() ? "重做 (" + undoManager.getRedoPresentationName() + ")" : "重做");
}

public void undo() {
    if (undoManager.canUndo()) {
        try { undoManager.undo(); } catch (CannotUndoException ignored) {}
    }
    updateUndoRedoState();
}

public void redo() {
    if (undoManager.canRedo()) {
        try { undoManager.redo(); } catch (CannotRedoException ignored) {}
    }
    updateUndoRedoState();
}

搜尋/取代演算法細節


// 向後搜尋
private void findNext(String kw, int start, int end) {
    String content = text.getText();
    int pos = content.indexOf(kw, start);
    if (pos >= 0 && pos + kw.length() <= end) {
        text.setCaretPosition(pos + kw.length());
        text.moveCaretPosition(pos);
    } else {
        JOptionPane.showMessageDialog(frame, "找不到 \"" + kw + "\"");
    }
}

// 向前搜尋
private void findPrevious(String kw, int start) {
    String content = text.getText();
    int pos = content.lastIndexOf(kw, start - 1);
    if (pos >= 0) {
        text.setCaretPosition(pos);
        text.moveCaretPosition(pos + kw.length());
    } else {
        JOptionPane.showMessageDialog(frame, "找不到 \"" + kw + "\"");
    }
}

// 取代 (單次)
private void replaceStr(String find, String replace, int start, int end) {
    String content = text.getText();
    int pos = content.indexOf(find, start);
    if (pos >= 0 && pos + find.length() <= end) {
        text.select(pos, pos + find.length());
        text.replaceSelection(replace);
    }
}

右鍵選單建構


JPopupMenu pop = new JPopupMenu();
JMenuItem miCut = new JMenuItem(new DefaultEditorKit.CutAction());
JMenuItem miCopy = new JMenuItem(new DefaultEditorKit.CopyAction());
JMenuItem miPaste = new JMenuItem(new DefaultEditorKit.PasteAction());
pop.add(miCut); pop.add(miCopy); pop.add(miPaste);
pop.addSeparator();
pop.add(new JMenuItem("全選", e -> text.selectAll()));
text.setComponentPopupMenu(pop); // JDK 5+ 直接關聯

格式/說明選單 (略)

  • 自動換行:text.setLineWrap(!text.getLineWrap())
  • 字體/顏色:JFontChooser / JColorChooser 彈窗套用至 text.setFont()、text.setForeground()
  • 關於對話框:JOptionPane.showMessageDialog(frame, "記事本 v1.0\nAuthor: Jeason")

總結

本篇完整呈現記事本核心邏輯:以 ActionListener 適配器分派事件、UndoManager 實作無限級撤銷、JFileChooser 處理檔案 I/O、DefaultEditorKit 簡化剪貼簿操作、自訂對話框支援搜尋取代。下一步可擴充:行號顯示、語法高亮、多標籤分頁、外掛架構等。

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