使用拖放重新排序 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&lt;String&gt; 示例解决该问题的示例。

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的主要内容,如果未能解决你的问题,请参考以下文章

使用拖放重新排序 ListView 中的项目

html 列表使用拖放和reorderArray重新排序

硒、水豚和黄瓜测试拖放重新排序

在 PyQt 中使用拖放重新排序 QTreeWidget 中的项目

使用拖放重新排序winforms列表框?

QML:如何通过拖放重新排序中继器项目?里面有一些工作代码