問題描述
我有幾個 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 的確切位置,而不是在底部的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!