您的位置:首页 > 运维架构

Swing Drag & Drop

2013-09-09 22:26 204 查看
“JFC Swing Tutorial, The: A Guide to Constructing GUIs, Second Editon”





JTextComponent在创建的时候会安装cut/copy/paste的按键绑定,从而支持这几种快捷键操作。

传输机制的核心是TransferHandler类,它提供了了一个简单的机制在控件之间传输数据。被传输的数据被打包进一个实现了Transferable的接口中。对于默认支持拖拽的控件已经有一个默认的TransferHandler,也可以通过调用控件的setTransferHandler来设置新的Handler,从而替换控件默认的数据传输行为,例如 可以让文本控件传输颜色信息,而不是文本。

如果想要给一个控件添加拖拽操作,需要给控件添加addMouseListener(); 当鼠标被按下后,TransferHandler通过以COPY为参数调用exportAsDrag方法来初始化拖拽操作。

public class DragMouseAdapter extends MouseAdapter {
public void mousePressed(MouseEvent e) {
JComponent c = (JComponent)e.getSource();
TransferHandler handler = c.getTransferHandler();
handler.exportAsDrag(c, e, TransferHandler.COPY);
}
}

label = new JLabel("I'm a Label!", SwingConstants.LEADING);
label.setTransferHandler(new TransferHandler("text"));

MouseListener listener = new DragMouseAdapter();
label.addMouseListener(listener);


TransferHandler中的importData和canImport方法处理导入数据; getSourceActions, createTransferable, exportDone方法处理导出数据。如果你不是CUT操作,就不需要实现exportDone方法,不用在传输结束后删除本地的数据。

public abstract class StringTransferHandler extends TransferHandler {
protected abstract String exportString(JComponent c);
protected abstract void importString(JComponent c, String str);
protected abstract void cleanup(JComponent c, boolean remove);

protected Transferable createTransferable(JComponent c) {
return new StringSelection(exportString(c));
}

public int getSourceActions(JComponent c) {
return COPY_OR_MOVE;
}

public boolean importData(JComponent c, Transferable t) {
if (canImport(c, t.getTransferDataFlavors())) {
try {
String str =
(String)t.getTransferData(DataFlavor.stringFlavor);
importString(c, str);
return true;
} catch (UnsupportedFlavorException ufe) {
} catch (IOException ioe) {
}
}
return false;
}

protected void exportDone(JComponent c,
Transferable data, int action){
cleanup(c, action == MOVE);
}

public boolean canImport(JComponent c, DataFlavor[] flavors) {
for (int i = 0; i < flavors.length; i++) {
if (DataFlavor.stringFlavor.equals(flavors[i])) {
return true;
}
}
return false;
}
}


其中的StringSelection类实现了Transferable接口,负责将传输数据进行打包。一个子类实现:

public class ListTransferHandler extends StringTransferHandler {
private int[] indices = null;
private int addIndex = -1; //Location where items were added
private int addCount = 0;  //Number of items added.

//Bundle up the selected items in the list
//as a single string, for export.
protected String exportString(JComponent c) {
JList list = (JList)c;
indices = list.getSelectedIndices();
Object[] values = list.getSelectedValues();
StringBuffer buff = new StringBuffer();

for (int i = 0; i < values.length; i++) {
Object val = values[i];
buff.append(val == null ? "" : val.toString());
if (i != values.length - 1) {
buff.append("\n");
}
}
return buff.toString();
}

//Take the incoming string and wherever there is a
//newline, break it into a separate item in the list.
protected void importString(JComponent c, String str) {
JList target = (JList)c;
DefaultListModel listModel = (DefaultListModel)target.getModel();
int index = target.getSelectedIndex();

//Prevent the user from dropping data back on itself.
//For example, if the user is moving items #4,#5,#6 and #7 and
//attempts to insert the items after item #5, this would
//be problematic when removing the original items.
//So this is not allowed.
if (indices != null && index >= indices[0] - 1 &&
index <= indices[indices.length - 1]) {
indices = null;
return;
}

int max = listModel.getSize();
if (index < 0) {
index = max;
} else {
index++;
if (index > max) {
index = max;
}
}
addIndex = index;
String[] values = str.split("\n");
addCount = values.length;
for (int i = 0; i < values.length; i++) {
listModel.add(index++, values[i]);
}
}

//If the remove argument is true, the drop has been
//successful and it's time to remove the selected items
//from the list. If the remove argument is false, it
//was a Copy operation and the original list is left
//intact.
protected void cleanup(JComponent c, boolean remove) {
if (remove && indices != null) {
JList source = (JList)c;
DefaultListModel model  = (DefaultListModel)source.getModel();
//If we are moving items around in the same list, we
//need to adjust the indices accordingly, since those
//after the insertion point have moved.
if (addCount > 0) {
for (int i = 0; i < indices.length; i++) {
if (indices[i] > addIndex) {
indices[i] += addCount;
}
}
}

for (int i = indices.length - 1; i >= 0; i--) {
model.remove(indices[i]);
}
}

indices = null;
addCount = 0;
addIndex = -1;
}
}


注意在删除原有元素时,需要根据被拖走元素位置,对要删除的元素索引进行适配,以免把新加入的元素删掉了。

DataFlavor类用来描述TransferHandler和Transferable支持的数据类型。系统预定义了三种:

imageFlavor----java.awt.Image

stringFlavor----java.lang.String

javaFileListFlavor-----java.io.File objects in a java.util.List

创建自定义的DataFlavor:

DataFlavor(Class representationClass, String humanPresentableName);  如

new DataFlavor(ArrayList.class, "ArrayList");

new DataFlavor(int[].class, "Integer Array");

通过DataFlavor方式传输的数据使用对象的serialization,因此传输的数据对象需要实现Serializable接口,否则会有NotSerializableException。

通过DataFlavor(Class, String)方式创建的DataFlavor可以在应用程序间传递数据,包括本地应用程序。如果想创建一个只在应用程序内部传输数据,可以使用javaJVMLoacalObjectMimeType 和DataFlavor(String)方法。例如创建一个Color类的DataFlavor:

String colorType = DataFlavor.javaJVMLocalOjectMimeType + “;class=java.awt.Color”;

DataFlavor colorFlavor = new DataFlavor(colorType);

整型数组可以用:

new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + ";class=\"" + int[].class.getName() + "\"");

一个包含有特殊字符如[和;的MIME type,需要将这些特殊字符放在引号中。

一个Transferable对象可以支持多个flavors。在实现Transferable的getTransferDataFlavors方法时,创建一个DataFlavors的数组,将最想支持的数据类型放在最前面。

自定义控件实现拖拽需要实现鼠标的MouseMotionListener接口。在mouseDragged方法中,判断光标在鼠标按下的条件下移动了n个像素时,就调用TransferHandler来初始化拖拽操作,同时可判断是否有按下Ctrl来确定是复制还是移动。

MouseEvent firstMouseEvent = null;

public void mousePressed(MouseEvent e) {
//Don't bother to drag if there is no image.
if (image == null) return;
firstMouseEvent = e;
e.consume();
}

public void mouseDragged(MouseEvent e) {
//Don't bother to drag if the component displays no image.
if (image == null) return;
if (firstMouseEvent != null) {
e.consume();
//If they are holding down the control key, COPY rather than MOVE
int ctrlMask = InputEvent.CTRL_DOWN_MASK;
int action = ((e.getModifiersEx() & ctrlMask) == ctrlMask) ?
TransferHandler.COPY : TransferHandler.MOVE;
int dx = Math.abs(e.getX() - firstMouseEvent.getX());
int dy = Math.abs(e.getY() - firstMouseEvent.getY());
//Arbitrarily define a 5-pixel shift as the
//official beginning of a drag.
if (dx > 5 || dy > 5) {
//This is a drag, not a click.
JComponent c = (JComponent)e.getSource();
//Tell the transfer handler to initiate the drag.
TransferHandler handler = c.getTransferHandler();
handler.exportAsDrag(c, firstMouseEvent, action);
firstMouseEvent = null;
}
}
}

public void mouseReleased(MouseEvent e) {
firstMouseEvent = null;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: