# JList: Reihenfolge der Elemente per Drag'n'Drop ändern.



## phiLue (16. Jul 2009)

Hallo 

Also ich bin neu hier im Forum und hab mich wegen nem Problem welches ich hab hier angemeldet. Ich versuch mich jetzt nun schon recht lange daran eine JList welche mit Objekten des Typs JLabel befüllt ist per Drag'n'Drop von den Elementen her in der Reihenfolge zu verschieben.

Wichtig ist das ich nicht einfach nur den Text kopieren kann weil das JLabel auch ein Bild enthält.

Im Forum hab ich mich auch schon eine Zeit lang umgeguckt, und auch ein paar Sachen gefunden, aber entweder haben Sie nicht das Problem beschrieben was ich hatte oder ich bekam den Fehler "Page not found".

Gegoogelt hab ich natürlich auch schon und auch mehrere Beispiele versucht, welche mich aber letzendlich immer wieder in eine Sackgasse gebracht haben.

Ich hoffe ich werde hier fündig und einer von euch kann mir das anhand eines Beispiels etwas genauer erklären. 

Vielen Dank schon einmal im voraus.


----------



## Ebenius (16. Jul 2009)

Das macht ja richtig Spaß. Ich hoffe, damit kannst Du was anfangen: 
	
	
	
	





```
/* (@)JListDnDFun.java */

/* Copyright 2009 Sebastian Haufe

 * Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       [url]http://www.apache.org/licenses/LICENSE-2.0[/url]

 * Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License. */

package com.ebenius;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Stack;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.border.SoftBevelBorder;

/**
 * Test application for list DnD.
 * 
 * @author Sebastian Haufe
 */
public class JListDnDFun {

  /**
   * Model bound data flavor.
   * 
   * @author Sebastian Haufe
   */
  static class ListMoveDataFlavor extends DataFlavor {

    private final DefaultListModel model;

    public ListMoveDataFlavor(DefaultListModel model) {
      super(ListMoveTransferData.class, "List Data");
      this.model = model;
    }

    public DefaultListModel getModel() {
      return model;
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = super.hashCode();
      result = prime * result + ((model == null) ? 0 : model.hashCode());
      return result;
    }

    @Override
    public boolean equals(DataFlavor that) {
      if (this == that) {
        return true;
      }
      if (!super.equals(that) || getClass() != that.getClass()) {
        return false;
      }
      return match(model, that);
    }

    /**
     * Tests whether the given data flavor is a {@link ListMoveDataFlavor} and
     * matches the given model.
     * 
     * @param model the model
     * @param flavor the flavor
     * @return {@code true} if matches
     */
    public static boolean match(DefaultListModel model, DataFlavor flavor) {
      return flavor instanceof ListMoveDataFlavor
            && ((ListMoveDataFlavor) flavor).getModel() == model;
    }
  }

  /**
   * Model bound and index based transfer data.
   * 
   * @author Sebastian Haufe
   */
  private static class ListMoveTransferData {

    private final DefaultListModel model;
    private final int[] indices;

    ListMoveTransferData(DefaultListModel model, int[] indices) {
      this.model = model;
      this.indices = indices;
    }

    int[] getIndices() {
      return indices;
    }

    public DefaultListModel getModel() {
      return model;
    }
  }

  /**
   * Model bound transferable implementation.
   * 
   * @author Sebastian Haufe
   */
  static class ListMoveTransferable implements Transferable {

    private final ListMoveTransferData data;

    public ListMoveTransferable(ListMoveTransferData data) {
      this.data = data;
    }

    public DataFlavor[] getTransferDataFlavors() {
      return new DataFlavor[] { new ListMoveDataFlavor(data.getModel()) };
    }

    public boolean isDataFlavorSupported(DataFlavor flavor) {
      return ListMoveDataFlavor.match(data.getModel(), flavor);
    }

    public Object getTransferData(DataFlavor flavor)
          throws UnsupportedFlavorException, IOException {
      if (!isDataFlavorSupported(flavor)) {
        throw new UnsupportedFlavorException(flavor);
      }
      return data;
    }
  }

  /**
   * List transfer handler.
   * 
   * @author Sebastian Haufe
   */
  static class ListMoveTransferHandler extends TransferHandler {

    /** Serial version UID */
    private static final long serialVersionUID = 6703461043403098490L;

    @Override
    public int getSourceActions(JComponent c) {
      final JList list = (JList) c;
      return list.getModel() instanceof DefaultListModel ? MOVE : NONE;
    }

    @Override
    public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
      if (!(comp instanceof JList)
            || !(((JList) comp).getModel() instanceof DefaultListModel)) {
        return false;
      }

      final DefaultListModel model =
            (DefaultListModel) ((JList) comp).getModel();
      for (DataFlavor f : transferFlavors) {
        if (ListMoveDataFlavor.match(model, f)) {
          return true;
        }
      }
      return false;
    }

    @Override
    protected Transferable createTransferable(JComponent c) {
      final JList list = (JList) c;
      final int[] selectedIndices = list.getSelectedIndices();
      return new ListMoveTransferable(new ListMoveTransferData(
            (DefaultListModel) list.getModel(), selectedIndices));
    }

    @Override
    public boolean importData(TransferHandler.TransferSupport info) {
      final Component comp = info.getComponent();
      if (!info.isDrop() || !(comp instanceof JList)) {
        return false;
      }
      final JList list = (JList) comp;
      final ListModel lm = list.getModel();
      if (!(lm instanceof DefaultListModel)) {
        return false;
      }

      final DefaultListModel listModel = (DefaultListModel) lm;
      final DataFlavor flavor = new ListMoveDataFlavor(listModel);
      if (!info.isDataFlavorSupported(flavor)) {
        return false;
      }

      final Transferable transferable = info.getTransferable();
      try {
        final ListMoveTransferData data =
              (ListMoveTransferData) transferable.getTransferData(flavor);

        // get the initial insertion index
        final JList.DropLocation dropLocation = list.getDropLocation();
        int insertAt = dropLocation.getIndex();

        // get the indices sorted (we use them in reverse order, below)
        final int[] indices = data.getIndices();
        Arrays.sort(indices);

        // remove old elements from model, store them on stack
        final Stack<Object> elements = new Stack<Object>();
        int shift = 0;
        for (int i = indices.length - 1; i >= 0; i--) {
          final int index = indices[i];
          if (index < insertAt) {
            shift--;
          }
          elements.push(listModel.remove(index));
        }
        insertAt += shift;

        // insert stored elements from stack to model
        final ListSelectionModel sm = list.getSelectionModel();
        try {
          sm.setValueIsAdjusting(true);
          sm.clearSelection();
          final int anchor = insertAt;
          while (!elements.isEmpty()) {
            listModel.insertElementAt(elements.pop(), insertAt);
            sm.addSelectionInterval(insertAt, insertAt++);
          }
          final int lead = insertAt - 1;
          if (!sm.isSelectionEmpty()) {
            sm.setAnchorSelectionIndex(anchor);
            sm.setLeadSelectionIndex(lead);
          }
        } finally {
          sm.setValueIsAdjusting(false);
        }
        return true;
      } catch (UnsupportedFlavorException ex) {
        return false;
      } catch (IOException ex) {
        // FIXME: Logging
        return false;
      }
    }
  }

  // -------------------------------------------------------------------------
  // Program Entry Point
  // -------------------------------------------------------------------------

  /**
   * Test main method.
   * 
   * @param args ignored
   */
  public static void main(String[] args) {
    final DefaultListModel lm1 = new DefaultListModel();
    final DefaultListModel lm2 = new DefaultListModel();
    for (Object o : new Object[] { "A", "B", "C", "D", "E", "F", "G", "H" }) {
      lm1.addElement(o);
      lm2.addElement(o);
    }
    final JComponent sp1 = createListAndScrollPane(lm1);
    final JComponent sp2 = createListAndScrollPane(lm2);
    final JComponent sp3 = createListAndScrollPane(lm2);

    final JPanel indiPanel = new JPanel(new BorderLayout(6, 6));
    indiPanel.add(sp1, BorderLayout.CENTER);
    indiPanel.setBorder(BorderFactory.createTitledBorder(new SoftBevelBorder(
          BevelBorder.LOWERED), "Independent Model"));

    final JPanel sharedPanel = new JPanel(new GridLayout(1, 0, 6, 6));
    sharedPanel.add(sp2);
    sharedPanel.add(sp3);
    sharedPanel.setBorder(BorderFactory.createTitledBorder(
          new SoftBevelBorder(BevelBorder.LOWERED), "Shared Model"));

    final JPanel contentPane = new JPanel(new BorderLayout(6, 6));
    contentPane.add(indiPanel, BorderLayout.LINE_START);
    contentPane.add(sharedPanel, BorderLayout.CENTER);

    final JFrame f = new JFrame("Test Frame: List DnD Fun"); //$NON-NLS-1$
    f.setContentPane(contentPane);
    f.pack();
    f.setLocationRelativeTo(null);
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.setVisible(true);
  }

  private static JScrollPane createListAndScrollPane(DefaultListModel model) {
    final JList list = new JList(model);
    list.setDragEnabled(true);
    list.setDropMode(DropMode.INSERT);
    list.setTransferHandler(new ListMoveTransferHandler());
    list.setPrototypeCellValue("WWWWWWWWWWWWWWWWWW");
    final JScrollPane sp = new JScrollPane(list);
    return sp;
  }
}
```
Ebenius


----------



## phiLue (17. Jul 2009)

Vielen herzlichen Dank, genau sowas hab ich gesucht. Bin sehr gut mit dem Quellcode klar gekommen, auch wenn es am Anfang recht verwirrend aussah.

Bei mir klappt nun alles einwand frei, danke nochmal, war wirklich am verzweifeln.


----------



## Verjigorm (17. Jul 2009)

Ebenius du bist verrückt :toll:

Das muss ich mir mal markern


----------



## Ebenius (17. Jul 2009)

Verjigorm hat gesagt.:


> Ebenius du bist verrückt :toll:


Wieso bin ich verrückt? Ich bin nur hauptberuflich Java-Entwickler, der 90% seiner Zeit mit Swing zubringt. Die Lösung oben hat mich zwei Stunden gekostet, ich hab was gelernt, und hab's heute schon in ähnlicher Form in eines unserer Produkte eingebaut (was leider etwas schwieriger war, weil es in JRE 5 noch keine DropLocations gibt).

Sport frei!

Ebenius


----------



## André Uhres (18. Jul 2009)

So ist's auch lustig  (zum Verschieben einzelner Zeilen):

```
MouseInputAdapter mouseHandler = new MouseInputAdapter() {
    private Object draggedObject;
    private int fromIndex;
    public void mousePressed(final MouseEvent evt) {
        draggedObject = list.getSelectedValue();
        fromIndex = list.getSelectedIndex();
    }
    public void mouseDragged(final MouseEvent evt) {
        int toIndex = list.locationToIndex(evt.getPoint());
        if (toIndex != fromIndex) {
            model.removeElementAt(fromIndex);
            model.insertElementAt(draggedObject, toIndex);
            fromIndex = toIndex;
        }
    }
};
list.addMouseListener(mouseHandler);
list.addMouseMotionListener(mouseHandler);
```


----------



## Ebenius (19. Jul 2009)

André Uhres hat gesagt.:


> So ist's auch lustig


Da fehlt dann aber die graphische Rückmeldung, die Selektion stimmt hinterher nicht mehr und die Liste scrollt auch nicht. Deswegen hab ich mich auf den DnD-Krempel gestürtzt.

Think big, like the Americans do. :-D

Ebenius


----------



## André Uhres (19. Jul 2009)

Ebenius hat gesagt.:


> Da fehlt dann aber die graphische Rückmeldung, die Selektion stimmt hinterher nicht mehr und die Liste scrollt auch nicht.


Die graphische Rückmeldung ist einfach die Verschiebung der Zeile. Bei mir stimmt die Selektion hinterher noch und die Liste scrollt auch.


----------



## Ebenius (19. Jul 2009)

Du single selection im SelectionModel, oder? Funktioniert das auch mit single/multi interval selection?

Ebenius


----------



## André Uhres (19. Jul 2009)

Ich habe mein Beispiel vielleicht schlecht beschrieben. Ich meinte, daß immer nur einzelne Zeilen verschoben werden, d.h. nur die angeklickte Zeile wird jeweils verschoben. Auf diese Funktion bezogen ist der SelectionMode egal.


----------

