拖放 是圖形介面常見互動模式。Java 提供 java.awt.dnd 與 java.awt.datatransfer 套件支援拖放操作。本文完整解析拖放實作機制,並提供可執行的 JDragAndDropDemo 範例:將標籤文字拖曳至文字欄位追加內容。
| 角色 | 介面/類 | 職責 |
|---|---|---|
| 拖曳來源 | DragSource, DragGestureListener, DragSourceListener | 提供資料、啟動拖曳、處理拖曳過程事件 |
| 放置目標 | DropTarget, DropTargetListener | 接收資料、處理放置事件 |
| 資料傳輸 | Transferable, DataFlavor | 封裝資料、定義資料格式 |
DragSource ds = DragSource.getDefaultDragSource(); int action = DnDConstants.ACTION_COPY_OR_MOVE; ds.createDefaultDragGestureRecognizer(this, action, this);
this:拖曳來源元件(需實作 DragGestureListener)action:拖曳動作類型(複製/移動)public void dragGestureRecognized(DragGestureEvent dge) {
try {
// 封裝要傳輸的資料
Transferable tr = new StringSelection(this.getText());
// 啟動拖曳:游標、資料、DragSourceListener
dge.startDrag(DragSource.DefaultCopyNoDrop, tr, this);
} catch (Exception e) { e.printStackTrace(); }
}
public void dragEnter(DragSourceDragEvent dsde) {
DragSourceContext dsc = dsde.getDragSourceContext();
int action = dsde.getDropAction();
// 根據放置動作設定游標
if ((action & DnDConstants.ACTION_COPY) != 0)
dsc.setCursor(DragSource.DefaultCopyDrop);
else
dsc.setCursor(DragSource.DefaultCopyNoDrop);
}
// dragOver, dropActionChanged, dragExit, dragDropEnd 可留空
new DropTarget(textField, DnDConstants.ACTION_COPY_OR_MOVE, this);
textField:放置目標元件this:實作 DropTargetListener 的監聽器public void drop(DropTargetDropEvent dtde) {
try {
Transferable tr = dtde.getTransferable();
// 檢查資料格式是否支援
if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor)) {
dtde.acceptDrop(dtde.getDropAction());
String s = (String) tr.getTransferData(DataFlavor.stringFlavor);
textField.setText(textField.getText() + s); // 追加文字
dtde.dropComplete(true);
} else {
dtde.rejectDrop();
}
} catch (Exception e) { e.printStackTrace(); }
}
// dragEnter, dragOver, dropActionChanged, dragExit 可留空
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
public class DragJLabel extends JLabel
implements DragGestureListener, DragSourceListener {
private DragSource ds = DragSource.getDefaultDragSource();
public DragJLabel(String text, int alignment) {
super(text, alignment);
int action = DnDConstants.ACTION_COPY_OR_MOVE;
ds.createDefaultDragGestureRecognizer(this, action, this);
}
@Override public void dragGestureRecognized(DragGestureEvent dge) {
try {
Transferable tr = new StringSelection(getText());
dge.startDrag(DragSource.DefaultCopyNoDrop, tr, this);
} catch (Exception e) { e.printStackTrace(); }
}
@Override public void dragEnter(DragSourceDragEvent dsde) {
DragSourceContext dsc = dsde.getDragSourceContext();
int action = dsde.getDropAction();
dsc.setCursor((action & DnDConstants.ACTION_COPY) != 0 ?
DragSource.DefaultCopyDrop : DragSource.DefaultCopyNoDrop);
}
// 其他 DragSourceListener 方法留空
@Override public void dragOver(DragSourceDragEvent e) {}
@Override public void dropActionChanged(DragSourceDragEvent e) {}
@Override public void dragExit(DragSourceEvent e) {}
@Override public void dragDropEnd(DragSourceDropEvent e) {}
}
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
public class MainFrame extends JFrame implements DropTargetListener {
private JTextField textField = new JTextField();
private DragJLabel label = new DragJLabel("Hello World!", SwingConstants.CENTER);
public MainFrame() {
initUI();
}
private void initUI() {
setLayout(new BorderLayout());
setSize(410, 114);
setTitle("Java Drag and Drop Demo");
setDefaultCloseOperation(EXIT_ON_CLOSE);
textField.setFont(new Font("Dialog", Font.PLAIN, 14));
add(textField, BorderLayout.SOUTH);
add(label, BorderLayout.NORTH);
// 註冊放置目標
new DropTarget(textField, DnDConstants.ACTION_COPY_OR_MOVE, this);
}
@Override public void drop(DropTargetDropEvent dtde) {
try {
Transferable tr = dtde.getTransferable();
if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor)) {
dtde.acceptDrop(dtde.getDropAction());
String s = (String) tr.getTransferData(DataFlavor.stringFlavor);
textField.setText(textField.getText() + s);
dtde.dropComplete(true);
} else { dtde.rejectDrop(); }
} catch (Exception e) { e.printStackTrace(); }
}
// 其他 DropTargetListener 方法留空
@Override public void dragEnter(DropTargetDragEvent e) {}
@Override public void dragOver(DropTargetDragEvent e) {}
@Override public void dropActionChanged(DropTargetDragEvent e) {}
@Override public void dragExit(DropTargetEvent e) {}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new MainFrame().setVisible(true));
}
}
DataFlavor.javaFileListFlavor 接收檔案清單DataFlavor.imageFlavor 接收圖片資料Transferable 定義專屬 DataFlavorJava AWT 拖放架構雖然 API 較繁瑣,但提供了完整的事件模型與資料傳輸抽象。核心在於:來源元件建立 DragGestureRecognizer、實作 DragGestureListener 啟動拖曳、目標元件建立 DropTarget、實作 DropTargetListener 接收資料。掌握此模式可實現豐富的桌面應用互動體驗。