2013-05-16 2 views
9

У меня возникла проблема с переупорядочиванием элементов в JList с помощью Drag and Drop. Этот следующий код является модификацией кода, в котором вы могли бы перетаскивать элементы из одного 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. Извините, если это окажется повторной отправкой.

EDIT Я думаю, что я получил это исправлено: было разным transferHandlers - должен иметь только один со всеми методами из второго, а также.

+3

* "но так как я даже не могу заставить его работать только с строками" * +1, попробовав его в более простой форме. Никогда не перестаешь удивлять меня, когда люди появляются здесь с 200+ LOC, содержащими множество нерелевантных трещин. –

ответ

12

См. Drop Demo из учебника Swing по DnD для примера, который будет падать на том же JList или другом JList.

+0

Спасибо! Наконец я заметил, почему он не работает. – UserOrNotAnUser

18

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

Спасибо за это, я бы никогда не смог сделать это без вашего сообщения :) – cbt

0

Как отметил О. П. в их правке оригинального вопрос, проблема в приведенном примере, что было два обработчика передачи и, как camickr справедливо указала в своем ответе, есть пример в учебниках Java, которые будут работать.

Проблема с примером в учебниках по Java заключается в том, что при использовании DropMode.INSERT и перемещении элемента в текущем JList до выбранного индекса, элемент дублируется. Это удаляет элемент в JList, помещает дубликат элемента в том месте, куда вы хотите его, и оставляет исходный выбранный элемент таким, какой он есть.

Таким образом, для тех, кого это интересует, приведен пример, который устраняет эту проблему, основанную на примере , представленном в вопросе ОП.

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()); 
    } 
} 
Смежные вопросы