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

Java Swing 拖放機制全攻略:DropTarget、DragSource 與內建 DnD 支援

     編輯:Java 相關知識

前言

拖放(Drag and Drop,DnD)是現代 GUI 應用不可或缺的交互方式。Java 在 AWT 階段即提供完整的 DnD 框架(java.awt.dnd),Swing 組件則進一步封裝簡化了常見操作。本文將系統講解 DropTarget(放置目標)、DragSource(拖曳源) 兩大核心類,並展示如何啟用 Swing 文字組件的內建拖放支援。

一、核心架構概覽

DnD 操作涉及三大角色:

  • DragSource:發起拖曳的組件,負責封裝 Transferable 資料物件
  • DropTarget:接收放置的組件,實作 DropTargetListener 處理放置邏輯
  • Transferable:封裝被拖曳資料的介面,支援多種 DataFlavor(資料格式)

流程:使用者按下滑鼠 → DragGestureRecognizer 識別手勢 → DragSource 啟動拖曳 → 使用者移動至目標 → DropTarget 觸發 dragEnter/dragOver/drop → 完成資料傳輸。

二、DropTarget:接收檔案與圖片放置

以下範例建立一個視窗,當使用者從檔案總管拖曳圖片檔案放入時,自動在內部視窗中顯示圖片。

完整範例:圖片放置目標

public class ImageDropTargetDemo {
    private static final int DESKTOP_W = 800;
    private static final int DESKTOP_H = 600;
    private static final int OFFSET = 30;

    private final JFrame frame = new JFrame("拖放目標示例:拖入圖片檔案");
    private final JDesktopPane desktop = new JDesktopPane();
    private int nextX = 0, nextY = 0;
    private final int frameW = DESKTOP_W / 2;
    private final int frameH = DESKTOP_H / 2;

    public void init() {
        desktop.setPreferredSize(new Dimension(DESKTOP_W, DESKTOP_H));

        // 關鍵:將 JFrame 註冊為 DropTarget
        new DropTarget(frame, DnDConstants.ACTION_COPY_OR_MOVE, new ImageDropHandler());

        frame.add(desktop);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private class ImageDropHandler extends DropTargetAdapter {
        @Override
        public void drop(DropTargetDropEvent event) {
            // 1. 接受複製或移動操作
            event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);

            // 2. 取得 Transferable 物件
            Transferable transferable = event.getTransferable();
            DataFlavor[] flavors = transferable.getTransferDataFlavors();

            // 3. 遍歷支援的資料格式
            for (DataFlavor flavor : flavors) {
                try {
                    // 4. 判斷是否為 Java 檔案列表格式
                    if (flavor.equals(DataFlavor.javaFileListFlavor)) {
                        @SuppressWarnings("unchecked")
                        List<File> fileList = (List<File>) transferable.getTransferData(flavor);
                        for (File file : fileList) {
                            showImage(file);
                        }
                        event.dropComplete(true);
                        return; // 處理完畢
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                    event.dropComplete(false);
                    return;
                }
            }
            // 無支援格式
            event.rejectDrop();
        }

        private void showImage(File file) {
            try {
                BufferedImage image = ImageIO.read(file);
                if (image == null) {
                    JOptionPane.showInternalMessageDialog(desktop, "不支援的圖片格式:" + file.getName());
                    return;
                }

                JInternalFrame iframe = new JInternalFrame(file.getName(), true, true, true, true);
                JLabel label = new JLabel(new ImageIcon(image));
                iframe.add(new JScrollPane(label));
                desktop.add(iframe);
                iframe.reshape(nextX, nextY, frameW, frameH);
                iframe.setVisible(true);

                nextX += OFFSET; nextY += OFFSET;
                if (nextX + frameW > desktop.getWidth()) nextX = 0;
                if (nextY + frameH > desktop.getHeight()) nextY = 0;
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new ImageDropTargetDemo().init());
    }
}

關鍵點解析

  1. DropTarget 建構式:new DropTarget(Component, int actions, DropTargetListener) 綁定目標組件、接受動作類型、監聽器
  2. DataFlavor.javaFileListFlavor:標準格式,表示拖曳資料為 java.util.List<File>
  3. event.acceptDrop / rejectDrop / dropComplete:必須呼叫其中之一結束拖放協議,否則會阻塞拖曳源
  4. 內部視窗管理:復用 JDesktopPane 實現多圖片視窗排列

三、DragSource:提供文字拖曳給外部應用

以下範例讓 JLabel 成為拖曳源,使用者可將其文字內容拖放到支援 DnD 的編輯器(如 IntelliJ IDEA、Eclipse、VS Code、WordPad)。注意:Windows 記事本預設不支援文字拖放。

完整範例:文字拖曳源

public class TextDragSourceDemo {
    public void init() {
        JFrame frame = new JFrame("DragSource 示例:將文字拖至外部編輯器");
        JLabel sourceLabel = new JLabel(
            "AWT 拖放支援演示
按住滑鼠左鍵拖動此標籤文字到支援 DnD 的編輯器(IDE、WordPad 等)"); sourceLabel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); sourceLabel.setOpaque(true); sourceLabel.setBackground(Color.LIGHT_GRAY); // 1. 取得預設 DragSource DragSource dragSource = DragSource.getDefaultDragSource(); // 2. 為組件建立拖曳手勢識別器 dragSource.createDefaultDragGestureRecognizer( sourceLabel, DnDConstants.ACTION_COPY_OR_MOVE, new DragGestureListener() { @Override public void dragGestureRecognized(DragGestureEvent dge) { // 3. 封裝文字為 Transferable String text = sourceLabel.getText().replaceAll("<[^>]+>", ""); // 移除 HTML 標籤 Transferable transferable = new StringSelection(text); // 4. 啟動拖曳:指定游標與 Transferable dge.startDrag(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR), transferable); } }); frame.add(new JScrollPane(sourceLabel)); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 200); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> new TextDragSourceDemo().init()); } }

關鍵點解析

  1. DragGestureRecognizer:識別使用者拖曳手勢(按下+移動),觸發 dragGestureRecognized
  2. StringSelection:實作 Transferable,自動支援 DataFlavor.stringFlavor 與 plainTextFlavor
  3. startDrag(Cursor, Transferable):啟動拖曳操作,游標可自訂(此處使用手型)
  4. 動作常數:ACTION_COPY、ACTION_MOVE、ACTION_LINK、ACTION_COPY_OR_MOVE

四、Swing 組件內建 DnD 支援(JDK 1.4+)

JDK 1.4 起,Swing 文字組件(JTextArea、JTextField、JEditorPane 等)內建拖放支援,預設關閉,需呼叫 setDragEnabled(true) 啟用。啟用後:

  • 拖出:選取文字 → 按住滑鼠拖曳 → 可放入外部編輯器或同應用其他文字組件
  • 拖入:從外部或同應用拖曳文字放入 → 自動插入游標位置

範例:啟用文字組件拖放

public class SwingBuiltInDnDDemo {
    public void init() {
        JFrame frame = new JFrame("Swing 內建 DnD:文字區域拖放");
        JTextArea textArea = new JTextArea(10, 40);
        JTextField textField = new JTextField(30);

        textArea.setText("Swing 內建拖放支援演示\n" +
                         "1. 選取文字拖曳到下方文字框或外部編輯器\n" +
                         "2. 從外部拖曳文字放入此區域\n" +
                         "3. 單行文字框同樣支援拖放");

        // 關鍵:啟用拖放
        textArea.setDragEnabled(true);
        textField.setDragEnabled(true);

        frame.add(new JScrollPane(textArea), BorderLayout.CENTER);
        frame.add(textField, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new SwingBuiltInDnDDemo().init());
    }
}

五、進階主題與最佳實踐

1. 自訂 Transferable 支援多種格式

實作 Transferable 介面,在 getTransferDataFlavors() 回傳支援的 DataFlavor 陣列,getTransferData() 根據請求格式回傳對應資料(如同時支援純文字、HTML、RTF)。

2. 拖曳反饋:DragSourceListener

實作 DragSourceListener 監聽 dragDropEnd 判斷拖曳是否成功(event.getDropSuccess()),執行後續邏輯(如移動操作需從源刪除資料)。

3. 放置反饋:DropTargetListener 完整實作

public class FullDropTargetListener implements DropTargetListener {
    @Override public void dragEnter(DropTargetDragEvent dtde) { /* 檢查格式、設定接受動作 */ }
    @Override public void dragOver(DropTargetDragEvent dtde) { /* 更新游標反饋 */ }
    @Override public void dropActionChanged(DropTargetDragEvent dtde) { }
    @Override public void dragExit(DropTargetEvent dte) { /* 清理視覺反饋 */ }
    @Override public void drop(DropTargetDropEvent dtde) { /* 執行資料傳輸 */ }
}

4. 與系統剪貼簿整合

Transferable 同時被剪貼簿(Clipboard)使用,實作一次即支援複製/貼上與拖放。

總結

類別/介面角色典型用法
DropTarget放置目標接收檔案、圖片、文字等外部資料
DragSource拖曳源提供組件資料供外部應用拖放
Transferable資料載體封裝多種 DataFlavor(StringSelection, FileList, Image 等)
DataFlavor資料格式javaFileListFlavor, stringFlavor, imageFlavor, 自訂 MIME 類型
JTextComponent.setDragEnabled(true)內建支援一鍵啟用文字組件雙向拖放

掌握 AWT DnD 框架核心類別與 Swing 內建支援,即可構建豐富的拖放交互體驗。建議從 setDragEnabled(true) 開始體驗,再逐步實作自訂 DropTarget 處理檔案放置,最後掌握 DragSource 輸出自訂格式資料。

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