问题描述
我有几个 JList 都可以作为 DnD COPY 操作的来源和目的地.它们工作得很好,但一方面 - 元素被添加到列表的底部,而不是我想要的放置目标行.
I've got a couple of JList both being able to be sources and destinations of DnD COPY actions. They work fine, but for one thing - elements are added at the bottom of the lists, instead at the drop destination row, as I'd like.
由于 Oracle BasicDnD 示例 包含此功能(事实上,它是我能够通过 Google 找到的唯一应用程序),我一直在查看它的 source 看看我是否能够去适应它.我试图设置 TransferHandler 对象,这是我想我缺少的,但没有出现新的行为.那么,我可能遗漏/做错了什么(其他)?
Since Oracle BasicDnD example includes this feature (and, in fact, it's the only application I've been able to find through Google with it), I've been taking a look at its source to see if I was able to adapt it. I tried to set the TransferHandler object, which is what I guess I'm missing, but no new behavior appeared. So, what (else) may I be missing/doing wrong?
下面显示了我用于这些列表的类.
Below is shown the class I use for these lists.
private class InteractiveJList extends JList implements DragGestureListener,
DragSourceListener, DropTargetListener {
private final DropTarget dropTarget;
private final DragSource dragSource;
private final boolean removeElementsOnFail;
private int[] selectedOnes;
@SuppressWarnings("unchecked")
private InteractiveJList(final ListModel model,
final boolean _removElementsOnFail) {
super(model);
this.dragSource = new DragSource();
this.dragSource
.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY,
this);
this.dropTarget = new DropTarget(this, this);
this.removeElementsOnFail = _removElementsOnFail;
}
@Override
public void dragEnter(final DropTargetDragEvent arg0) {
}
@Override
public void dragExit(final DropTargetEvent arg0) {
}
@Override
public void dragOver(final DropTargetDragEvent arg0) {
}
@Override
public void drop(final DropTargetDropEvent event) {
if (!this.removeElementsOnFail) {
event.rejectDrop();
return;
}
final Transferable transferable = event.getTransferable();
if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {
String all;
try {
all = (String) transferable.getTransferData(DataFlavor.stringFlavor);
} catch (final UnsupportedFlavorException | IOException ex) {
event.rejectDrop();
return;
}
event.acceptDrop(DnDConstants.ACTION_COPY);
final StringTokenizer tokenizer = new StringTokenizer(all, "|");
while (tokenizer.hasMoreTokens()) {
((StringListModel) this.getModel()).addElement(tokenizer.nextToken());
}
event.getDropTargetContext().dropComplete(Boolean.TRUE);
} else {
event.rejectDrop();
}
}
@Override
public void dropActionChanged(final DropTargetDragEvent event) {
}
@Override
public void dragEnter(final DragSourceDragEvent arg0) {
}
@Override
public void dragExit(final DragSourceEvent arg0) {
}
@Override
public void dragOver(final DragSourceDragEvent arg0) {
}
@Override
public void dropActionChanged(final DragSourceDragEvent dragSourceEvent) {
}
@Override
public void dragGestureRecognized(final DragGestureEvent dragGestureEvent) {
final Object listSelectedValue = this.getSelectedValue();
this.selectedOnes = this.getSelectedIndices();
final StringBuilder bridge = new StringBuilder();
for (final int x : this.selectedOnes) {
bridge.append(this.getModel().getElementAt(x)).append("|");
}
if (listSelectedValue != null) {
final StringSelection stringTransferable =
new StringSelection(bridge.toString()
.substring(0, bridge.length() - 1));
this.dragSource.startDrag(dragGestureEvent, DragSource.DefaultCopyDrop,
stringTransferable, this);
}
}
@Override
public void dragDropEnd(final DragSourceDropEvent dragSourceDropEvent) {
if (!dragSourceDropEvent.getDropSuccess()) {
if (this.removeElementsOnFail) {
for (final int x : this.selectedOnes) {
((StringListModel) this.getModel()).removeElement(x);
}
}
}
}
}
使用构造函数中的布尔标志是因为,对于其中一个列表,如果丢弃操作被拒绝,则必须删除该元素,但不能在另一个列表中删除该元素.这样我就可以处理了.StringListModel
是 AbstractListModel 来处理字符串列表.它能够在底部和请求的位置添加元素,清除列表并删除某些元素.它做自己的事情,触发相应的事件,仅此而已.
The boolean flag in the constructor is used because, for one of the lists, if the drop action if rejected, the element must be removed, but not in the another. This way I can handle that. StringListModel
is an extension of AbstractListModel to handle list of Strings. It is able to add elements both at bottom and at a requested position, clears the list, and remove certain elements. It does its stuff, fires the corresponding events and nothing further.
下面是我对 MadProgrammer 建议的实现:
Below is shown my implementation of MadProgrammer's suggestion:
private static class UserTransferHandler extends TransferHandler {
private final JList list;
private UserTransferHandler(final JList list) {
this.list = list;
}
@Override
public boolean canImport(final TransferSupport support) {
System.out.println("canImport");
boolean canImport = false;
if (support.isDataFlavorSupported(UserTransferable.USER_DATA_FLAVOR)) {
final JList.DropLocation dl =
(JList.DropLocation) support.getDropLocation();
if (dl.getIndex() != -1) {
canImport = true;
}
}
return canImport;
}
@Override
protected void exportDone(final JComponent source, final Transferable data,
final int action) {
System.out.println("exportDone");
}
@Override
public boolean importData(final TransferSupport support) {
System.out.println("importData");
boolean accepted = false;
if (support.isDrop()) {
if (support.isDataFlavorSupported(UserTransferable.USER_DATA_FLAVOR)) {
final JList.DropLocation dl =
(JList.DropLocation) support.getDropLocation();
final StringListModel model = (StringListModel) this.list.getModel();
final int index = dl.getIndex();
final boolean insert = dl.isInsert();
final Transferable t = support.getTransferable();
try {
final String dropped = (String) t
.getTransferData(UserTransferable.USER_DATA_FLAVOR);
if (insert) {
if (index >= model.getSize()) {
model.addElement(dropped);
} else {
model.addElementAt(dropped, index);
}
} else {
model.addElement(dropped);
}
accepted = true;
} catch (final Exception e) {
e.printStackTrace();
}
}
}
return accepted;
}
@Override
public int getSourceActions(final JComponent c) {
System.out.println("getSourceActions");
return TransferHandler.COPY_OR_MOVE;
}
@Override
protected Transferable createTransferable(final JComponent c) {
System.out.println("createTransferable");
final String value = this.list.getSelectedValue().toString();
return new UserTransferable(value);
}
}
private static class UserTransferable implements Transferable {
private static final DataFlavor USER_DATA_FLAVOR =
new DataFlavor(String.class, "String");
private final String value;
private UserTransferable(final String value) {
System.out.println("UserTransferable");
this.value = value;
}
@Override
public DataFlavor[] getTransferDataFlavors() {
System.out.println("getTransferDataFlavors");
return new DataFlavor[] { UserTransferable.USER_DATA_FLAVOR };
}
@Override
public boolean isDataFlavorSupported(final DataFlavor flavor) {
System.out.println("isDataFlavorSupported");
return UserTransferable.USER_DATA_FLAVOR.equals(flavor);
}
@Override
public Object getTransferData(final DataFlavor flavor) throws
UnsupportedFlavorException, IOException {
System.out.println("getTransferData");
if (!UserTransferable.USER_DATA_FLAVOR.equals(flavor)) {
throw new UnsupportedFlavorException(flavor);
}
return this.value;
}
}
打印语句用于在调用方法时获得反馈(现在永远不会).
The print statements are there to get feedback when the methods are called (which is, right now, never).
推荐答案
这是一个使用(较新的)Transferable
API 的完整工作示例.这是基于您链接的示例.
This is a fully working example that uses the (newer) Transferable
API. This is based on the example you have linked.
请注意,我没有使用 String
的麻烦,因为这只会让人们感到困惑,相反,我正在传输对象.
Note, I've not bothered with using String
s, as this just confuses people, instead, I'm transferring objects.
另请注意,我使用的是移动"API,因此名称将从一个列表移动到另一个列表...
Also note, that I'm using a "move" API, so names will be moved from one list to the other...
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class ComponentAPIDnD {
public static void main(String[] args) {
new ComponentAPIDnD();
}
public ComponentAPIDnD() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JList left;
private JList right;
public TestPane() {
setLayout(new GridLayout(0, 2));
DefaultListModel<User> model = new DefaultListModel<>();
model.addElement(new User("Rai"));
model.addElement(new User("Mark"));
model.addElement(new User("Han"));
model.addElement(new User("Luke"));
model.addElement(new User("Ben"));
model.addElement(new User("Yoda"));
left = new JList(model);
left.setCellRenderer(new UserListCellRenderer());
right = new JList(new DefaultListModel<User>());
right.setCellRenderer(new UserListCellRenderer());
left.setName("Left");
right.setName("Right");
add(new JScrollPane(left));
add(new JScrollPane(right));
left.setTransferHandler(new UserTransferHandler(left));
right.setTransferHandler(new UserTransferHandler(right));
left.setDropMode(DropMode.ON_OR_INSERT);
right.setDropMode(DropMode.ON_OR_INSERT);
left.setDragEnabled(true);
right.setDragEnabled(true);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public static class UserTransferHandler extends TransferHandler {
private JList list;
public UserTransferHandler(JList list) {
this.list = list;
}
@Override
public boolean canImport(TransferSupport support) {
boolean canImport = false;
if (support.isDataFlavorSupported(UserTransferable.USER_DATA_FLAVOR)) {
JList.DropLocation dl = (JList.DropLocation) support.getDropLocation();
if (dl.getIndex() != -1) {
canImport = true;
}
}
return canImport;
}
@Override
protected void exportDone(JComponent source, Transferable data, int action) {
try {
User user = (User) data.getTransferData(UserTransferable.USER_DATA_FLAVOR);
((DefaultListModel<User>)list.getModel()).removeElement(user);
} catch (UnsupportedFlavorException | IOException ex) {
ex.printStackTrace();
}
}
@Override
public boolean importData(TransferSupport support) {
boolean accepted = false;
if (support.isDrop()) {
if (support.isDataFlavorSupported(UserTransferable.USER_DATA_FLAVOR)) {
JList.DropLocation dl = (JList.DropLocation)support.getDropLocation();
DefaultListModel<User> model = (DefaultListModel<User>) list.getModel();
int index = dl.getIndex();
boolean insert = dl.isInsert();
Transferable t = support.getTransferable();
try {
User dropped = (User) t.getTransferData(UserTransferable.USER_DATA_FLAVOR);
System.out.println("Drop " + dropped + " on " + list.getName());
if (insert) {
if (index >= model.getSize()) {
model.addElement(dropped);
} else {
model.add(index, dropped);
}
} else {
model.addElement(dropped);
}
accepted = true;
} catch (Exception e) {
e.printStackTrace();
}
}
}
return accepted;
}
@Override
public int getSourceActions(JComponent c) {
return COPY_OR_MOVE;
}
@Override
protected Transferable createTransferable(JComponent c) {
User user = (User) list.getSelectedValue();
return new UserTransferable(user);
}
}
public static class UserTransferable implements Transferable {
public static final DataFlavor USER_DATA_FLAVOR = new DataFlavor(User.class, "User");
private User user;
public UserTransferable(User user) {
this.user = user;
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] {USER_DATA_FLAVOR};
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return USER_DATA_FLAVOR.equals(flavor);
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
Object value = null;
if (USER_DATA_FLAVOR.equals(flavor)) {
value = user;
} else {
throw new UnsupportedFlavorException(flavor);
}
return user;
}
}
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class UserListCellRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof User) {
setText(((User) value).getName());
}
return this;
}
}
}
这篇关于将字符串添加到 JList 的确切位置,而不是在底部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!