Reordering JList with Drag and Drop

前端 未结 3 1941
余生分开走
余生分开走 2020-12-11 03:02

I encountered a problem regarding reordering elements in a JList using Drag and Drop. This following code is a modification of a code where you could drag elements from one

相关标签:
3条回答
  • 2020-12-11 03:07

    As the OP noted in their edit to the original question, the problem in the example given was that there were two transfer handlers and as camickr rightly pointed out in their answer, there is an example in the Java Tutorials which will work.

    The problem with the example in the Java Tutorials is that, when using DropMode.INSERT and moving an item in the current JList to before the selected index, the item is duplicated. This deletes an item in the JList, puts a duplicate of the item in the place you wanted it to go, and leaves the original selected item as it is.

    So, for those interested, here is an example which fixes that problem based upon the JList<String> example provided in the OP's question.

    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());
        }
    }
    
    0 讨论(0)
  • 2020-12-11 03:13

    enter image description here

    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;
      }
    }
    
    0 讨论(0)
  • 2020-12-11 03:25

    See the Drop Demo from the Swing tutorial on DnD for an example that will drop on the same JList or another JList.

    0 讨论(0)
提交回复
热议问题