使用拖放重新排序 JList

2022-01-11 00:00:00 drag-and-drop java swing java-7 jlist

我遇到了一个关于使用拖放对 JList 中的元素进行重新排序的问题.以下代码是对代码的修改,您可以在其中将元素从一个 JList 拖到另一个(仅以一种方式工作).我试图让它只对一个 JList 可用,但元素甚至不能被拖出列表.所以我想它不能这样做.任何想法我做错了什么或没有考虑到?

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 JList to another (worked only one way). I tried to make it usable for only one JList, but the elements can't even be dragged out of the list. So I guess it can't be done this way. Any ideas what I'm doing wrong or not taking into consideration?

这个想法是让它为带有缩略图的 Jlist 工作,但因为我什至不能让它只用字符串工作......我一直在研究几个 D'n'D 教程,但我仍然无法让它工作.任何帮助表示赞赏.

The idea is to get it to work for a Jlist with thumbnails, but since I can't even get it to work with just strings... I have been looking into several D'n'D tutorials, but still I can't get it to work. Any help is appreciated.

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.抱歉,如果这是一个重新发布.

PS. Sorry if this turns out to be a re-post.

编辑我想我已经解决了:必须使用不同的 transferHandlers - 应该只有一个包含第二个中的所有方法.

EDIT I think I got it fixed: had to different transferHandlers - should only have one with all the methods from the second one as well.

推荐答案

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;
  }
}

相关文章