2013-05-16 16 views
9

Ho riscontrato un problema relativo al riordino di elementi in una JList mediante trascinamento della selezione. Questo codice seguente è una modifica di un codice in cui è possibile trascinare elementi da una JList a un'altra (ha funzionato solo in un modo). Ho provato a renderlo utilizzabile per una sola JList, ma gli elementi non possono nemmeno essere trascinati fuori dalla lista. Quindi immagino che non possa essere fatto in questo modo. Qualche idea su cosa sto sbagliando o non prendendo in considerazione?Riordino di JList con trascinamento della selezione

L'idea è farla funzionare per una Jlist con le anteprime, ma dato che non riesco nemmeno a farlo funzionare con le sole stringhe ... Ho esaminato diversi tutorial di D'n'D, ma ancora Non riesco a farlo funzionare. Qualsiasi aiuto è apprezzato.

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. Scusa se questo risulta essere un nuovo post.

EDIT Penso che ho avuto è fisso: doveva diversi transferHandlers - dovrebbe avere una sola con tutti i metodi del secondo uno pure.

+3

* "ma poiché non riesco nemmeno a farlo funzionare con le sole stringhe" * +1 per provarlo nella forma più semplice 1st. Non smette mai di stupirmi quando le persone arrivano qui con 200+ LOC contenenti un sacco di irrilevanti cruft. –

risposta

12

Vedere il Drop Demo dal tutorial Swing su DnD per un esempio che verrà rilasciato sullo stesso JList o su un'altra JList.

+0

Grazie! Finalmente ho notato perché non funzionava. – 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

Grazie per questo, non potrei mai farlo senza il tuo post :) – cbt

0

Come il PO osservato nel loro modifica alla domanda originale, il problema nell'esempio fornito era che c'erano due gestori di trasferimento e come camickr giustamente indicate nella loro risposta, v'è un esempio in Java Tutorial che funzionerà.

Il problema con l'Esercitazione Java nell'esempio è che, quando si utilizza DropMode.INSERT e si sposta un elemento nell'attuale JList prima dell'indice selezionato, l'elemento viene duplicato. Questo elimina un elemento nel JList, inserisce un duplicato dell'elemento nel punto in cui si desidera farlo e lascia l'elemento selezionato originale così com'è.

Quindi, per chi fosse interessato, ecco un esempio che corregge quel problema basato sull'esempio JList<String> fornito nella domanda dell'OP.

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()); 
    } 
} 
Problemi correlati