DnD(Drag And Drop)对于程序的用户友好度至关重要
常见的, 把一个文件直接拖入程序里, 把item从一个list拖到另一个list
最几天在学习Swing的DnD机制, 被搞得一塌糊涂, 晕头转向的忙活了好几个晚上, 终于理出一点头绪来:
总体来说, DnD 包括这几个类: DragSource, DragGestureRecognizer, DragGestureListener, DragSourceAdapter, DropTargetAdapter, TransferHandler, Transferable, DataFlavor
DragSoure: 环境变量, 生成DragGestureRecognizer
DragGestureRecognizer: 环境变量, 绑定DragGestureListener给指定的Jcomponent
DragGestureListener: 拖拽事件开始的源头
DragSourceAdapter: JComponent Source Listener: dragEnter, dragOver, dragExit, dragDropEnd
DropTargetAdapter: JComponent Target Listener: dropEnter,, drapOver, dragExit, drop
TransferHandler: drag & drop event handler, 从官方的源代码设计来看, DragSourceAdapter, DropTargetAdapter都包含在里面, 类似代理器的设计模式
Transferable: DnD事件的统一数据接口
DataFlavor:DnD事件的统一数据
先以简单的例子来演示DnD的机制
一 Drag
1.给 JTextField 添加DnD:
JTextField txt1=new JTextField();
txt1.setDragEnabled(true);
虽然只需以上2行代码即可, 但其实Swing在背后给我们做了很多事情
首先, jframe生成的时候, DataSourse和DragGestureRecognizer已经生成,
并给所有textfield, textarea之类的控件添加了DnD事件,
其中所有的jtextfield控件都绑定给
"javax.swing.plaf.basic.BasicTextUI$TextTransferHandler@4a63d8",
而这个类的内部也生成有DragSoure和DropTarget所有接口的实现
然后, 当你在jtextfield里
选中所有文字, 再拖拽的时候, 就会激活DnD事件
具体顺序(Drag):
TransferHandler exportAsDrag->getSourceActions->createTransferabled->exportDone->dragGestureRecognized
多尝试几次, 你会发现, 其实
不选中所有文件而直接拖拽, 也会激活DnD事件
TransferHandler dragGestureRecognized
更不可思议的是, 哪怕 txt1.setDragEnabled(false)也一样, 这是很有用的
2.给 Jlabel 添加DnD
官方给出的演示
/**
* add label drag action with mousePressed
* @author YS
*/
public class DragAndDropDemo2 {
JFrame frame;
Container container;
JLabel lbl1;
JLabel lbl2;
JTextField txt1;
JTextField txt2;
JLabel lbl3;
JTextArea txt3;
public DragAndDropDemo2() {
frame = new JFrame("DRAG AND DROP DEMO");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
container = frame.getContentPane();
frame.setBounds(100, 100, 400, 300);
addComponent();
frame.setVisible(true);
}
private void addComponent() {
lbl1 = new JLabel("text1");
lbl2 = new JLabel("text2");
txt1 = new JTextField();
txt2 = new JTextField();
lbl1.setBounds(10, 10, 40, 20);
lbl2.setBounds(10, 40, 40, 20);
txt1.setBounds(60, 10, 300, 20);
txt2.setBounds(60, 40, 300, 20);
txt1.setDragEnabled(true);
txt2.setDragEnabled(true);
container.add(lbl1);
container.add(lbl2);
container.add(txt1);
container.add(txt2);
addLabelDragEvent(lbl1);
}
private void addLabelDragEvent(JLabel lbl1) {
lbl1.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
JComponent com=(JComponent)e.getSource();
TransferHandler hander=new TransferHandler("text");
com.setTransferHandler(hander);
hander.exportAsDrag(com, e, DnDConstants.ACTION_COPY);
}
});
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DragAndDropDemo2();
}
});
}
}
由于label不能设计setDragEnabled(true), 所以需要人为的激活DnD事件, 一开始时, jframe并没有为jlabel绑定transferhanlder, 所以必须自己创建, 在new TransferHandler("text")的同时, drag & drop事件也已经生成了, 带"text"参数, 是创建能接受文本内容的handler, 具体的swing已经为我们做好了, 能用预设的最好都用预设的, 然后在mousePressed时激活exportAsDrag, 然后再转到 handler内部的dragGestureRecognized接口实现, swing已经帮我们作好获取label里面文本并生成transferable的工作了, 最终调用内部的startDrag开始拖拽
自己写另外一种实现
/**
* add label drag action with dragGestureRecognizedListener
* @author YS
*/
public class DragAndDropDemo3 {
JFrame frame;
Container container;
JLabel lbl1;
JLabel lbl2;
JTextField txt1;
JTextField txt2;
JLabel lbl3;
JTextArea txt3;
public DragAndDropDemo3() {
frame = new JFrame("DRAG AND DROP DEMO");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
container = frame.getContentPane();
frame.setBounds(100, 100, 400, 300);
addComponent();
frame.setVisible(true);
}
private void addComponent() {
lbl1 = new JLabel("text1");
lbl2 = new JLabel("text2");
txt1 = new JTextField();
txt2 = new JTextField();
lbl1.setBounds(10, 10, 40, 20);
lbl2.setBounds(10, 40, 40, 20);
txt1.setBounds(60, 10, 300, 20);
txt2.setBounds(60, 40, 300, 20);
txt1.setDragEnabled(true);
txt2.setDragEnabled(true);
container.add(lbl1);
container.add(lbl2);
container.add(txt1);
container.add(txt2);
addLabelDragEvent(lbl1);
}
private void addLabelDragEvent(JLabel lbl1) {
DragSource ds=DragSource.getDefaultDragSource();
ds.createDefaultDragGestureRecognizer(lbl1, DnDConstants.ACTION_COPY, new DragGestureAdapter());
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DragAndDropDemo3();
}
});
}
class DragGestureAdapter implements DragGestureListener{
public void dragGestureRecognized(DragGestureEvent e) {
System.out.println("Drag dragGestureRecognized");
JComponent c = (JComponent) e.getComponent();
Transferable t=createTransferable(c);
e.startDrag(DragSource.DefaultCopyNoDrop, t, new DragAdapter());
}
}
class DragAdapter extends DragSourceAdapter{
@Override
public void dragEnter(DragSourceDragEvent dsde) {
Component com=(Component)dsde.getDragSourceContext().getComponent();
Set<Class> set=new LinkedHashSet<Class>();
set.add(JLabel.class);
set.add(JTextField.class);
if(set.contains(com.getClass())){
dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
}
}
@Override
public void dragExit(DragSourceEvent dse) {
Component com=(Component)dse.getDragSourceContext().getComponent();
Set<Class> set=new LinkedHashSet<Class>();
set.add(JLabel.class);
set.add(JTextField.class);
if(set.contains(com.getClass())){
dse.getDragSourceContext().setCursor(DragSource.DefaultCopyNoDrop);
}
}
}
protected Transferable createTransferable(JComponent c) {
System.out.println("DragGestureAdapter createTransferable");
//return super.createTransferable(c);
String text="";
Component com=c;
String type=com.getClass().getSimpleName();
if("JLabel".equals(type)){
text=type+((JLabel)com).getText();
}else if("JTextField".equals(type)){
text=type+": "+((JTextField)com).getText();
}else if("JTextArea".equals(type)){
text=type+": "+((JTextArea)com).getText();
}
Transferable t=new StringSelection(text);
return t;
}
}
这样也可以实现同样的效果, 原因在于: 其实mouseDragged的时候, 也会判断dragGestureRecognized, 因为这里给label绑定了dragGestureRecognized接口的实现, 所以DnD机制还是开始了, 从startDrag开始
这里要说一下鼠标的手势, 一开始应当指定的是DragSource.DefaultCopyNoDrop, 会是一个不可复制的鼠标, 如果指定DragSource.DefaultCopyDrop的话, 就是一个带+号的复制鼠标, 这样会误人视听, 鼠标在进入别的控件时, 会激发dragEnter事件, 在离开控件时, 激活dragExit, 要注意鼠标的手势
对比以上3个例子, 有几个比较重要的地方,
jtextfield; setDragEnabled(true),全选并拖拽时激发transferhandler的exportDrag, 然后才是createTransferabled, dragDestrueGeconized, 最终startDrag, (startDrag是在dragDestrueGeconized里调用的)
label(官方): 通过mousePress, 人为调用exportDrag, 然后的过程跟jtextfield一样
label(自定义): 通过绑定dragDestrueGeconized给jlabel, 然后dragDestrueGeconized在鼠标拖拽时被激活, transferable必须在dragDestrueGeconized里生成
所以, 可以得到, dragDestrueGeconized接口总会在鼠标拖拽(非mousePress)时
-----------------------------------------------Separator-----------------------------------------------
二 Drop
Drop的触发顺序: 目标控件的DropTarget的 drop(松开鼠标左键)->获取目标控件的TransferHandler, 并调用它的canImport, 如果为true, 则执行acceptDrop ,然后调用importData, 最终dropComplete
仔细分析, 里面其实有2个地方容易混乱, 一个是droptarget的drop方法, 另一个是transferhandler的importData方法,
2个方法都可以给我们重载代码, 但要通过drop模似swing的路线走到importData是比较困难的, 而且importData方法是后面, 所以, 推荐优先尝试在importData里面实现我们要的东西
简单示例
1. 从JTextField拖曳内容给JLabel, TransferHandler实现
/**
* add label drop action with transferhandler("text")
* @author YS
*/
public class DragAndDropDemo4 {
JFrame frame;
Container container;
JLabel lbl1;
JLabel lbl2;
JTextField txt1;
JTextField txt2;
JLabel lbl3;
JTextArea txt3;
public DragAndDropDemo4() {
frame = new JFrame("DRAG AND DROP DEMO");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
container = frame.getContentPane();
frame.setBounds(100, 100, 400, 300);
addComponent();
frame.setVisible(true);
}
private void addComponent() {
lbl1 = new JLabel("text1");
lbl2 = new JLabel("text2");
txt1 = new JTextField();
txt2 = new JTextField();
lbl1.setBounds(10, 10, 40, 20);
lbl2.setBounds(10, 40, 40, 20);
txt1.setBounds(60, 10, 300, 20);
txt2.setBounds(60, 40, 300, 20);
txt1.setDragEnabled(true);
txt2.setDragEnabled(true);
container.add(lbl1);
container.add(lbl2);
container.add(txt1);
container.add(txt2);
addLabelDropEvent(lbl1);
}
private void addLabelDropEvent(JLabel lbl1) {
lbl1.setTransferHandler(new TransferHandler("text"));
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DragAndDropDemo4();
}
});
}
}
其实, 由于label一开始是没有transferhandler的, 所以只需要通过系统预设的transferhandler("text"), 与此同时生成了droptarget实例, 方便地实现了label的拖曳赋值
其系统机制: 当鼠标在label上released时, droptarget的drop方法被激活, 然后获取自身的transferhandler, 如果null, 则调用rejectDrop, 否则执行transferhandler.canImport判断这个控件是否能接受drop, 可以的话, 执行transferhandler.importData
2. 从JTextField拖曳内容给JLabel, DropTarget实现
/**
* add label drop action with DropTarget
* @author YS
*/
public class DragAndDropDemo5 {
JFrame frame;
Container container;
JLabel lbl1;
JLabel lbl2;
JTextField txt1;
JTextField txt2;
JLabel lbl3;
JTextArea txt3;
public DragAndDropDemo5() {
frame = new JFrame("DRAG AND DROP DEMO");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
container = frame.getContentPane();
frame.setBounds(100, 100, 400, 300);
addComponent();
frame.setVisible(true);
}
private void addComponent() {
lbl1 = new JLabel("text1");
lbl2 = new JLabel("text2");
txt1 = new JTextField();
txt2 = new JTextField();
lbl1.setBounds(10, 10, 40, 20);
lbl2.setBounds(10, 40, 40, 20);
txt1.setBounds(60, 10, 300, 20);
txt2.setBounds(60, 40, 300, 20);
txt1.setDragEnabled(true);
txt2.setDragEnabled(true);
container.add(lbl1);
container.add(lbl2);
container.add(txt1);
container.add(txt2);
addLabelDropEvent(lbl1);
}
private void addLabelDropEvent(JLabel lbl1) {
lbl1.setDropTarget(new MyDropTargetAdapter());
}
class MyDropTargetAdapter extends DropTarget{
@Override
public void drop(DropTargetDropEvent e) {
try {
e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
Component com=e.getDropTargetContext().getComponent();
Transferable t=e.getTransferable();
String text=(String)t.getTransferData(DataFlavor.stringFlavor);
if(com instanceof JLabel){
((JLabel)com).setText(text);
}else if(com instanceof JTextField){
((JTextField)com).setText(text);
}
e.dropComplete(true);
} catch (Exception ex) {
e.rejectDrop();
ex.printStackTrace();
}
}
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DragAndDropDemo5();
}
});
}
}
由于DropTarget的drop已经实现我想要的, 并且没有像swing那样全面的检查并调用transferhandler的canImport和importData, 所以, 整个drop动作就到此结束
注意e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE), 如果没有这个, 就表示没有接受drop, swing就不会给你transable和dataflavor,
但在transferhandler的importData里, 在importData之前, 就swing就已经帮我们判断好acceptDrop还是rejectDrop, 结束后还有dropComplete
通过上面2种方法都可以实现drop动作, 但出于代码的严整性和程序的整体观念考虑, 还是推荐第一种, 在transferhandler的importData里面实现
to be continue...
分享到:
相关推荐
java swing控件的drag和drop的实现方法: 主要用到java.awt.dnd.*;里面的方法,可以实现任意2个component的drag and drop.
drag and drop 拖放库 vue dnd mobile
# Beautiful-and-Accessible-Drag-and-Drop-with-react-beautiful-dnd-notes 讲师 描述 拖放 (dnd) 体验通常用于对内容列表进行垂直和水平排序。 react-beautiful-dnd 是这些用例的绝佳工具。 它利用渲染道具模式...
注意:v-drag-drop的2.x及更高版本仅与Vue 3兼容。 如果使用Vue 2,请安装1.x版。 旨在封装本机拖放API的某些特性,并使其更易于与Vue.js一起使用。 还添加了一些方便的功能,例如名称空间。 目录 安装 安装v-drag-...
Swing当中组件的拖拽原理,深度分析。在Windows环境的jvm进程中,一个gui程序将启动两个线程:AWT-WINDOWS(AWT)和Event-Dispatch-Thread(EDT)。
拖放HTML5 DnD 的快速示例。
React拖放应用 具有拖放功能的简单React应用
jQuery的样本拖放 通过拖放在jQuery中对元素进行排序,并使用Cookie记忆它们 图书馆 jQuery的 Sortable.js jquery.cookie.js
多列表传输的打字稿示例。 是吗? ? 下载示例。 git clone git@github.com:abeaudoin2013/react-beautiful-dnd-multi-list-typescript-example.git ... 这是来自React Beautiful DnD的的链接。
TodoList应用设置直播: : 要运行此项目,请使用npm在本地安装它:$ cd ../project_folder$ npm install$ npm start基本...技术领域使用以下项目创建项目: ReactJS React路由器React美丽的DND 上下文关联样式化的组件
我只是觉得自己很难理解react-dnd和react-beautiful-dnd。 它们都具有出色的功能和支持,非常出色。 我需要一个库,该库可以让我快速进行拖放操作并进行自定义,如果有任何错误,我将不会花费太多时间来理解该库。 ...
[Angular 1.x]按树显示treeDnD和事件DrapnDrop,字段“ td”。</ s> </ s> </ s> ###支持(仅当我在线时)演示: ###需求: angular: >=1.2.1来源(> = 3.0.4) ...安装凉亭: bower install angular - tree - ...
dnd txt文档阅读 提供目录 快速查询
Vue平滑DnD 用于Vue.js的快速,轻巧的拖放式可排序库,具有许多配置选项,涵盖许多d&d场景。 该库由库组成的包装Vue.js组件。演示版在此处查看演示:安装npm i vue-smooth-dnd用法Vue < template> < div> ...
本地js-dnd-list :alien_monster: 用TypeScript编写的Drag'n'Drop列表具有零依赖性。 支持移动设备 演示: : 项目设置 安装依赖项 yarn 跑步 yarn dev 皮棉 yarn lint
DnD原型 组件说明 原型允许产生标记,这将帮助医生保存有关患者特定区域的信息。 功能说明 用户可以输入要指出的内容的描述,然后将其添加到图形中,这些标记将显示将鼠标悬停在其上时输入的内容。 用户还将能够将...
DND中文手册,有TXT与HTML两种版本
react-dnd demo示例
react项目使用react-dnd实现拖拽排序
这个是DND跑团投骰子的PC端程序,包含D3至D100常见的ROLL点方式。 直接编译即可。