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

Swing 文本組件統一右鍵選單實現方案

     編輯:Java 相關知識

前言

在許多軟體應用中,文本組件(如 JTextField、JTextArea、JEditorPane、JTextPane 等)都需要提供統一的右鍵選單功能,包含剪下、複製、貼上、刪除、全選等操作。若為每個組件單獨編寫繼承類或添加滑鼠監聽器,不僅代碼冗餘,還難以維護。本文介紹一種通過攔截事件隊列實現全域統一右鍵選單的優雅方案。

核心原理:事件隊列攔截

Swing 所有事件均進入 java.awt.EventQueue 隊列,經由 dispatchEvent 方法分發。我們可繼承 EventQueue 並重寫 dispatchEvent 方法,攔截所有滑鼠事件,針對觸發彈出選單的動作(isPopupTrigger()),動態為所有繼承自 JTextComponent 的組件構建統一選單。

完整實現代碼

package org.kissjava.swingx.core;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JPopupMenu;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;

public class KJEventQueue extends EventQueue {
    @Override
    protected void dispatchEvent(AWTEvent event) {
        super.dispatchEvent(event);

        // 僅處理滑鼠事件
        if (!(event instanceof MouseEvent)) return;

        MouseEvent me = (MouseEvent) event;

        // 僅處理彈出選單觸發事件
        if (!me.isPopupTrigger()) return;

        // 獲取最深層組件
        Component comp = SwingUtilities.getDeepestComponentAt(me.getComponent(), me.getX(), me.getY());

        // 僅處理文本組件
        if (!(comp instanceof JTextComponent)) return;

        // 若已有選單顯示則跳過
        if (MenuSelectionManager.defaultManager().getSelectedPath().length > 0) return;

        // 建立統一彈出選單
        JTextComponent tc = (JTextComponent) comp;
        JPopupMenu menu = new JPopupMenu();
        menu.add(new CutAction(tc));
        menu.add(new CopyAction(tc));
        menu.add(new PasteAction(tc));
        menu.add(new DeleteAction(tc));
        menu.addSeparator();
        menu.add(new SelectAllAction(tc));

        Point pt = SwingUtilities.convertPoint(me.getComponent(), me.getPoint(), tc);
        menu.show(tc, pt.x, pt.y);
    }

    // 剪下動作
    class CutAction extends AbstractAction {
        JTextComponent comp;
        public CutAction(JTextComponent comp) { super("Cut"); this.comp = comp; }
        public void actionPerformed(ActionEvent e) { comp.cut(); }
        public boolean isEnabled() {
            return comp.isEditable() && comp.isEnabled() && comp.getSelectedText() != null;
        }
    }

    // 貼上動作
    class PasteAction extends AbstractAction {
        JTextComponent comp;
        public PasteAction(JTextComponent comp) { super("Paste"); this.comp = comp; }
        public void actionPerformed(ActionEvent e) { comp.paste(); }
        public boolean isEnabled() {
            if (comp.isEditable() && comp.isEnabled()) {
                Transferable contents = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(this);
                return contents.isDataFlavorSupported(DataFlavor.stringFlavor);
            }
            return false;
        }
    }

    // 刪除動作
    class DeleteAction extends AbstractAction {
        JTextComponent comp;
        public DeleteAction(JTextComponent comp) { super("Delete"); this.comp = comp; }
        public void actionPerformed(ActionEvent e) { comp.replaceSelection(null); }
        public boolean isEnabled() {
            return comp.isEditable() && comp.isEnabled() && comp.getSelectedText() != null;
        }
    }

    // 複製動作
    class CopyAction extends AbstractAction {
        JTextComponent comp;
        public CopyAction(JTextComponent comp) { super("Copy"); this.comp = comp; }
        public void actionPerformed(ActionEvent e) { comp.copy(); }
        public boolean isEnabled() {
            return comp.isEnabled() && comp.getSelectedText() != null;
        }
    }

    // 全選動作
    class SelectAllAction extends AbstractAction {
        JTextComponent comp;
        public SelectAllAction(JTextComponent comp) { super("Select All"); this.comp = comp; }
        public void actionPerformed(ActionEvent e) { comp.selectAll(); }
        public boolean isEnabled() {
            return comp.isEnabled() && comp.getText().length() > 0;
        }
    }
}

使用方法

在主程式入口(main 方法)中加入以下代碼即可啟用:

public static void main(String[] args) {
    Toolkit.getDefaultToolkit().getSystemEventQueue().push(new KJEventQueue());
    SwingUtilities.invokeLater(() -> new KJEventQueueDemo());
}

效果展示

優勢總結

  • 零侵入式:無需修改現有文本組件代碼
  • 統一維護:選單邏輯集中管理,易於擴展
  • 自動適配:適用於所有繼承 JTextComponent 的組件
  • 狀態感知:選單項根據組件狀態(可編輯、有選區、剪貼簿內容)自動啟用/禁用
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved