使用拖放重新排序 JList
Posted
技术标签:
【中文标题】使用拖放重新排序 JList【英文标题】:Reordering JList with Drag and Drop 【发布时间】:2013-05-11 07:04:34 【问题描述】:我遇到了关于使用拖放对 JList 中的元素进行重新排序的问题。下面的代码是对代码的修改,您可以将元素从一个 JList 拖到另一个(仅以一种方式工作)。我试图让它只对一个 JList 可用,但这些元素甚至不能被拖出列表。所以我想它不能这样做。任何想法我做错了什么或没有考虑到?
这个想法是让它为带有缩略图的 Jlist 工作,但因为我什至不能让它只用字符串工作......我一直在研究几个 D'n'D 教程,但我仍然无法让它工作。 任何帮助表示赞赏。
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.datatransfer.*;
import java.io.IOException;
public class DragAndDrop extends JFrame
DefaultListModel<String> transport = new DefaultListModel<String>();
JList<String> transportList = new JList<String>(transport);
public DragAndDrop()
setLayout(new FlowLayout());
transport.addElement("Bike");
transport.addElement("Car");
transport.addElement("Truck");
transport.addElement("Boat");
JScrollPane transportScroll = new JScrollPane(transportList);
transportScroll.setBorder(new TitledBorder("Transportation"));
add(transportScroll);
transportList.setDragEnabled(true);
transportList.setTransferHandler(new TransferHandler()
int index;
@Override
public int getSourceActions(JComponent comp)
return COPY_OR_MOVE;
@Override
public Transferable createTransferable(JComponent comp)
index = transportList.getSelectedIndex();
return new StringSelection(transportList.getSelectedValue());
@Override
public void exportDone( JComponent comp, Transferable trans, int action )
if (action==MOVE)
transport.remove(index);
);
transportList.setDropMode(DropMode.ON);
transportList.setTransferHandler(new TransferHandler()
@Override
public boolean canImport(TransferHandler.TransferSupport support)
// data of type string?
return support.isDataFlavorSupported(DataFlavor.stringFlavor);
@Override
public boolean importData(TransferHandler.TransferSupport support)
try
// convert data to string
String s = (String)support.getTransferable().getTransferData(DataFlavor.stringFlavor);
JList.DropLocation dl = (JList.DropLocation)support.getDropLocation();
transport.add(dl.getIndex(),s);
return true;
catch (UnsupportedFlavorException e)
catch (IOException e)
return false;
);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
public static void main(String[] args)
new DragAndDrop();
PS。抱歉,如果这是一个重新发布。
编辑 我想我已经解决了:必须使用不同的 transferHandlers - 应该只有一个包含第二个中的所有方法。
【问题讨论】:
“但是因为我什至不能让它只使用字符串” +1 以更简单的形式尝试它。当人们带着 200 多个 LOC 包含许多不相关的垃圾出现在这里时,我永远不会停止惊奇。 【参考方案1】:import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DragSource;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Objects;
// import javax.activation.ActivationDataFlavor;
// import javax.activation.DataHandler;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.DropMode;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
public class DragAndDropTest
public JComponent makeUI()
DefaultListModel<Thumbnail> m = new DefaultListModel<>();
for (String s : Arrays.asList("error", "information", "question", "warning"))
m.addElement(new Thumbnail(s));
JList<Thumbnail> list = new JList<>(m);
list.getSelectionModel().setSelectionMode(
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
list.setTransferHandler(new ListItemTransferHandler());
list.setDropMode(DropMode.INSERT);
list.setDragEnabled(true);
// https://java-swing-tips.blogspot.com/2008/10/rubber-band-selection-drag-and-drop.html
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(0);
list.setFixedCellWidth(80);
list.setFixedCellHeight(80);
list.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
list.setCellRenderer(new ListCellRenderer<Thumbnail>()
private final JPanel p = new JPanel(new BorderLayout());
private final JLabel icon = new JLabel((Icon)null, JLabel.CENTER);
private final JLabel label = new JLabel("", JLabel.CENTER);
@Override
public Component getListCellRendererComponent(
JList<? extends Thumbnail> list, Thumbnail value, int index,
boolean isSelected, boolean cellHasFocus)
icon.setIcon(value.icon);
label.setText(value.name);
label.setForeground(isSelected ? list.getSelectionForeground()
: list.getForeground());
p.add(icon);
p.add(label, BorderLayout.SOUTH);
p.setBackground(isSelected ? list.getSelectionBackground()
: list.getBackground());
return p;
);
return new JScrollPane(list);
public static void main(String[] args)
EventQueue.invokeLater(() -> createAndShowGUI());
public static void createAndShowGUI()
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new DragAndDropTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
class Thumbnail implements Serializable
public final String name;
public final Icon icon;
public Thumbnail(String name)
this.name = name;
this.icon = UIManager.getIcon("OptionPane." + name + "Icon");
// @camickr already suggested above.
// https://docs.oracle.com/javase/tutorial/uiswing/dnd/dropmodedemo.html
@SuppressWarnings("serial")
class ListItemTransferHandler extends TransferHandler
protected final DataFlavor localObjectFlavor;
protected int[] indices;
protected int addIndex = -1; // Location where items were added
protected int addCount; // Number of items added.
public ListItemTransferHandler()
super();
// localObjectFlavor = new ActivationDataFlavor(
// Object[].class, DataFlavor.javaJVMLocalObjectMimeType, "Array of items");
localObjectFlavor = new DataFlavor(Object[].class, "Array of items");
@Override
protected Transferable createTransferable(JComponent c)
JList<?> source = (JList<?>) c;
c.getRootPane().getGlassPane().setVisible(true);
indices = source.getSelectedIndices();
Object[] transferedObjects = source.getSelectedValuesList().toArray(new Object[0]);
// return new DataHandler(transferedObjects, localObjectFlavor.getMimeType());
return new Transferable()
@Override public DataFlavor[] getTransferDataFlavors()
return new DataFlavor[] localObjectFlavor;
@Override public boolean isDataFlavorSupported(DataFlavor flavor)
return Objects.equals(localObjectFlavor, flavor);
@Override public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException
if (isDataFlavorSupported(flavor))
return transferedObjects;
else
throw new UnsupportedFlavorException(flavor);
;
@Override
public boolean canImport(TransferSupport info)
return info.isDrop() && info.isDataFlavorSupported(localObjectFlavor);
@Override
public int getSourceActions(JComponent c)
Component glassPane = c.getRootPane().getGlassPane();
glassPane.setCursor(DragSource.DefaultMoveDrop);
return MOVE; // COPY_OR_MOVE;
@SuppressWarnings("unchecked")
@Override
public boolean importData(TransferSupport info)
TransferHandler.DropLocation tdl = info.getDropLocation();
if (!canImport(info) || !(tdl instanceof JList.DropLocation))
return false;
JList.DropLocation dl = (JList.DropLocation) tdl;
JList target = (JList) info.getComponent();
DefaultListModel listModel = (DefaultListModel) target.getModel();
int max = listModel.getSize();
int index = dl.getIndex();
index = index < 0 ? max : index; // If it is out of range, it is appended to the end
index = Math.min(index, max);
addIndex = index;
try
Object[] values = (Object[]) info.getTransferable().getTransferData(localObjectFlavor);
for (int i = 0; i < values.length; i++)
int idx = index++;
listModel.add(idx, values[i]);
target.addSelectionInterval(idx, idx);
addCount = values.length;
return true;
catch (UnsupportedFlavorException | IOException ex)
ex.printStackTrace();
return false;
@Override
protected void exportDone(JComponent c, Transferable data, int action)
c.getRootPane().getGlassPane().setVisible(false);
cleanup(c, action == MOVE);
private void cleanup(JComponent c, boolean remove)
if (remove && Objects.nonNull(indices))
if (addCount > 0)
// https://github.com/aterai/java-swing-tips/blob/master/DragSelectDropReordering/src/java/example/MainPanel.java
for (int i = 0; i < indices.length; i++)
if (indices[i] >= addIndex)
indices[i] += addCount;
JList source = (JList) c;
DefaultListModel model = (DefaultListModel) source.getModel();
for (int i = indices.length - 1; i >= 0; i--)
model.remove(indices[i]);
indices = null;
addCount = 0;
addIndex = -1;
【讨论】:
谢谢你,没有你的帖子我永远做不到:)【参考方案2】:请参阅 DnD 上的 Swing 教程中的 Drop Demo,以获取将放在同一个 JList 或另一个 JList 上的示例。
【讨论】:
【参考方案3】:正如 OP 在对原始问题的编辑中指出的那样,给出的示例中的问题是有两个传输处理程序,并且正如 camickr 在他们的回答中正确指出的那样,Java 教程中有一个示例将工作。
Java 教程中示例的问题是,当使用DropMode.INSERT
并将当前JList
中的项目移动到所选索引之前,该项目被复制。这将删除 JList
中的一个项目,将该项目的副本放在您希望它去的位置,并保持原来的选定项目不变。
因此,对于那些感兴趣的人,这里是一个基于 OP 问题中提供的 JList<String>
示例解决该问题的示例。
import java.awt.EventQueue;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import javax.swing.DefaultListModel;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.TransferHandler;
@SuppressWarnings("serial")
public class GUI extends JFrame
protected GUI()
super("Simple Rearrangeable List");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
createPanel();
setBounds(10, 10, 350, 500);
setVisible(true);
private void createPanel()
DefaultListModel<String> strings = new DefaultListModel<String>();
for(int i = 1; i <= 100; i++)
strings.addElement("Item " + i);
JList<String> dndList = new JList<String>(strings);
dndList.setDragEnabled(true);
dndList.setDropMode(DropMode.INSERT);
dndList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
dndList.setTransferHandler(new TransferHandler()
private int index;
private boolean beforeIndex = false; //Start with `false` therefore if it is removed from or added to the list it still works
@Override
public int getSourceActions(JComponent comp)
return MOVE;
@Override
public Transferable createTransferable(JComponent comp)
index = dndList.getSelectedIndex();
return new StringSelection(dndList.getSelectedValue());
@Override
public void exportDone(JComponent comp, Transferable trans, int action)
if (action == MOVE)
if(beforeIndex)
strings.remove(index + 1);
else
strings.remove(index);
@Override
public boolean canImport(TransferHandler.TransferSupport support)
return support.isDataFlavorSupported(DataFlavor.stringFlavor);
@Override
public boolean importData(TransferHandler.TransferSupport support)
try
String s = (String) support.getTransferable().getTransferData(DataFlavor.stringFlavor);
JList.DropLocation dl = (JList.DropLocation) support.getDropLocation();
strings.add(dl.getIndex(), s);
beforeIndex = dl.getIndex() < index ? true : false;
return true;
catch (UnsupportedFlavorException | IOException e)
e.printStackTrace();
return false;
);
JScrollPane scrollPane = new JScrollPane(dndList);
getContentPane().add(scrollPane);
public static void main(String[] args)
EventQueue.invokeLater(() -> new GUI());
【讨论】:
【参考方案4】:package draganddrop;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.datatransfer.*;
import java.io.IOException;
public class DragAndDrop extends JFrame
DefaultListModel<String> transport = new DefaultListModel<String>();
JList<String> transportList = new JList<String>(transport);
public DragAndDrop()
setLayout(new FlowLayout());
transport.addElement("Bike");
transport.addElement("Car");
transport.addElement("Truck");
transport.addElement("Boat");
JScrollPane transportScroll = new JScrollPane(transportList);
transportScroll.setBorder(new TitledBorder("Transportation"));
add(transportScroll);
transportList.setDragEnabled(true);
transportList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
transportList.setDropMode(DropMode.ON);
transportList.setTransferHandler(new TransferHandler()
int index;
int preindex;
@Override
public int getSourceActions(JComponent comp)
preindex = transportList.getSelectedIndex();
return MOVE;
@Override
public Transferable createTransferable(JComponent comp)
index = transportList.getSelectedIndex();
return new StringSelection(transportList.getSelectedValue());
@Override
public boolean canImport(TransferHandler.TransferSupport support)
// data of type string?
return support.isDataFlavorSupported(DataFlavor.stringFlavor);
@Override
public boolean importData(TransferHandler.TransferSupport support)
try
if (support.isDrop())
String s = (String) support.getTransferable().getTransferData(DataFlavor.stringFlavor);
JList.DropLocation dl = (JList.DropLocation) support.getDropLocation();
if (index < dl.getIndex())
transport.add(dl.getIndex() + 1, s);
transport.remove(index);
transportList.setSelectedIndex(dl.getIndex());
else
transport.add(dl.getIndex(), s);
transport.remove(index + 1);
transportList.setSelectedIndex(dl.getIndex());
preindex = transportList.getSelectedIndex();
return true;
else
index = transportList.getSelectedIndex();
if (preindex < transportList.getSelectedIndex())
transport.add(transportList.getSelectedIndex() + 1, transport.getElementAt(preindex));
transport.remove(preindex);
transportList.setSelectedIndex(index);
else
transport.add(transportList.getSelectedIndex(), transport.getElementAt(preindex));
transport.remove(preindex + 1);
transportList.setSelectedIndex(index);
preindex = transportList.getSelectedIndex();
return true;
catch (UnsupportedFlavorException e)
catch (IOException e)
return false;
);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
public static void main(String[] args)
new DragAndDrop();
试试这个
【讨论】:
以上是关于使用拖放重新排序 JList的主要内容,如果未能解决你的问题,请参考以下文章