Временно отключите или предотвратите перерисовку JViewPort при прокрутке с помощью мыши.

Я написал MouseListener, как определено ниже, чтобы я мог перемещать JButton, чтобы изменить порядок компонентов, которые находятся внутри JPanel. JPanel находится внутри JScrollPane, поэтому при добавлении нескольких компонентов их можно прокручивать.

Проблема, с которой я сталкиваюсь, заключается в том, что при перетаскивании компонента и выходе мыши из области прокрутки/окна просмотра компонент возвращается к своей позиции в пределах JPanel, а затем отрисовывается в правильном месте. Я предполагаю, что это поведение связано с тем, что Viewport вызывает перерисовку своих дочерних элементов, когда я вызываю scrollRectToVisible()

Есть ли способ предотвратить это?

Обратите внимание, что я ограничен Java 5

Слушатель

import java.awt.Component;
import java.awt.Container;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;

public class DragListener extends MouseInputAdapter
{
    private Point location;
    private MouseEvent pressed;
    private MouseEvent dragged;
    private MouseEvent dropped;

    @Override
    public void mousePressed(MouseEvent me)
    {
        pressed = me;
    }

    @Override
    public void mouseDragged(MouseEvent me)
    {
        dragged = me;
        Component component = dragged.getComponent();
        Container parent = component.getParent();
        Container superParent = parent.getParent();

        if(superParent instanceof JViewport)
        {
            JViewport vp = (JViewport)superParent;
            Rectangle vpb = vp.getBounds();
            Point pt = MouseInfo.getPointerInfo().getLocation();
            SwingUtilities.convertPointFromScreen(pt, vp);

            if(!vpb.contains(pt))
            {
                int yDiff = (pt.y < vpb.y ) ? pt.y : pt.y - vpb.height;
                vpb.translate(0, yDiff);
                vp.scrollRectToVisible(vpb);
            }
        }

        location = component.getLocation(location);
        int x = location.x - pressed.getX() + me.getX();
        int y = location.y - pressed.getY() + me.getY();
        component.setLocation(x, y);
    }

    // Mouse release omitted
}

Графический интерфейс (создан в NetBeans)

import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;
import javax.swing.JPanel;

public class DragginTest extends javax.swing.JFrame
{
    public DragginTest()
    {
        initComponents();
        addListeners(jButton1, jButton2, jButton3, jButton4, jButton5, jButton6, jButton7, jButton8, jButton9);
    }

    private void addListeners(JButton... buttons)
    {
        DragListener drag = new DragListener();
        for(JButton b : buttons)
        {
            b.addMouseListener(drag);
            b.addMouseMotionListener(drag); 
        }   
    }

    @SuppressWarnings("unchecked")

    private void initComponents()
    {
        jLayeredPane1 = new javax.swing.JLayeredPane();
        jScrollPane1 = new javax.swing.JScrollPane();
        mainPanel = new javax.swing.JPanel();
        jButton1 = new javax.swing.JButton();
        jButton2 = new javax.swing.JButton();
        jButton3 = new javax.swing.JButton();
        jButton4 = new javax.swing.JButton();
        jButton5 = new javax.swing.JButton();
        jButton6 = new javax.swing.JButton();
        jButton7 = new javax.swing.JButton();
        jButton8 = new javax.swing.JButton();
        jButton9 = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setPreferredSize(new java.awt.Dimension(450, 450));

        mainPanel.setLayout(new java.awt.GridLayout(5, 2, 2, 2));

        // Below Repeated for buttons 1-9 (left out for conciseness)
        jButton1.setFont(new java.awt.Font("Tahoma", 1, 48)); // NOI18N
        jButton1.setForeground(new java.awt.Color(255, 0, 0));
        jButton1.setText("1");
        mainPanel.add(jButton1);
        // End Repeat

        jScrollPane1.setViewportView(mainPanel);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
            .addGap(40, 40, 40)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 205, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(38, 38, 38))
        );
        layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addGap(40, 40, 40)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 226, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addContainerGap(53, Short.MAX_VALUE))
        );

        pack();
    }

    public static void main(String args[])
    {
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                new DragginTest().setVisible(true);
            }
        });
    }

    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JButton jButton3;
    private javax.swing.JButton jButton4;
    private javax.swing.JButton jButton5;
    private javax.swing.JButton jButton6;
    private javax.swing.JButton jButton7;
    private javax.swing.JButton jButton8;
    private javax.swing.JButton jButton9;
    private javax.swing.JLayeredPane jLayeredPane1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JPanel mainPanel;
}

person Java Devil    schedule 25.05.2015    source источник
comment
Скорее всего, это проблема с выходом мыши из компонента/окна просмотра и входом в область JScrollPane.   -  person MadProgrammer    schedule 25.05.2015
comment
Проблема, скорее всего, в диспетчере компоновки, который обновляется при прокрутке представления...   -  person MadProgrammer    schedule 25.05.2015


Ответы (2)


Я добавил хак в ваш код DragListener. По сути, он удаляет менеджер компоновки, пока вы перетаскиваете, поэтому повторная проверка ничего не делает, и восстанавливает менеджер компоновки, когда отпускаете мышь:

import java.awt.*;
import java.awt.Container;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;

public class DragListener extends MouseInputAdapter
{
    private Point location;
    private MouseEvent pressed;
    private MouseEvent dragged;
    private MouseEvent dropped;
    private LayoutManager layout;

    @Override
    public void mousePressed(MouseEvent me)
    {
        pressed = me;
        Component component = me.getComponent();
        Container parent = component.getParent();
        parent.setPreferredSize(parent.getPreferredSize());
        layout = parent.getLayout();
        parent.setLayout(null);
    }

    @Override
    public void mouseDragged(MouseEvent me)
    {
        dragged = me;
        Component component = dragged.getComponent();
        Container parent = component.getParent();
        Container superParent = parent.getParent();

        if(superParent instanceof JViewport)
        {
            JViewport vp = (JViewport)superParent;
            Rectangle vpb = vp.getBounds();
            Point pt = MouseInfo.getPointerInfo().getLocation();
            SwingUtilities.convertPointFromScreen(pt, vp);

            if(!vpb.contains(pt))
            {
                int yDiff = (pt.y < vpb.y ) ? pt.y : pt.y - vpb.height;
                vpb.translate(0, yDiff);
                vp.scrollRectToVisible(vpb);
            }
        }

        location = component.getLocation(location);
        int x = location.x - pressed.getX() + me.getX();
        int y = location.y - pressed.getY() + me.getY();
        component.setLocation(x, y);
    }

    // Mouse release omitted
    @Override
    public void mouseReleased(MouseEvent me)
    {
        Component component = me.getComponent();
        Container parent = component.getParent();
        parent.setPreferredSize( null );
        parent.setLayout(layout);
        parent.validate();
        parent.repaint();
    }
}

Конечно, я предполагаю, что ваш реальный код mouseReleased будет иметь логику для вставки кнопки в соответствующее место в контейнере, чтобы ее реальное местоположение могло поддерживаться GridLayout, иначе компонент просто вернется в исходное местоположение.

Редактировать:

Вот версия, которая перемещает кнопку на новое место при отпускании кнопки мыши. Немного сложно, потому что вам нужно беспокоиться о ZOrder. То есть перетаскивание компонента вниз — это нормально. Но если вы попытаетесь перетащить компонент вверх, он будет окрашен ниже других кнопок. Временный сброс ZOrder решает эту проблему.

Мальчик, код начинает быть большим взломом :) Временный нулевой макет и временный ZOrder.

В любом случае вот код:

import java.awt.*;
import java.awt.Container;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;

public class DragListener extends MouseInputAdapter
{
    private Point location;
    private MouseEvent pressed;
    private MouseEvent dragged;
    private MouseEvent dropped;
    private LayoutManager layout;
    private Rectangle originalBounds;
    private int originalZOrder;

    @Override
    public void mousePressed(MouseEvent me)
    {
        pressed = me;
        Component component = me.getComponent();
        Container parent = component.getParent();
        originalBounds = component.getBounds();
        originalZOrder = parent.getComponentZOrder(component);
        parent.setPreferredSize(parent.getPreferredSize());
        layout = parent.getLayout();
        parent.setLayout(null);
        parent.setComponentZOrder(component, 0);
    }

    @Override
    public void mouseDragged(MouseEvent me)
    {
        JComponent source = (JComponent) me.getComponent();
        JComponent parent = (JComponent) source.getParent();

        Point p = me.getPoint();
        p = SwingUtilities.convertPoint(source, p, parent);

        Rectangle bounds = source.getBounds();
        bounds.setLocation(p);

        bounds.x -= pressed.getX();
        bounds.y -= pressed.getY();
        source.setLocation(0, bounds.y);
        parent.scrollRectToVisible(bounds);
    }

    @Override
    public void mouseReleased(MouseEvent me)
    {
        boolean moved = false;
        Component component = me.getComponent();
        Container parent = component.getParent();
        Point location = component.getLocation();

        if (location.y < 0)
        {
            parent.add(component, 0);
            moved = true;
        }
        else
        {
            for (int i = 0; i < parent.getComponentCount(); i++)
            {
                Component c = parent.getComponent(i);
                Rectangle bounds = c.getBounds();

                if (c == component)
                    bounds = originalBounds;

                //  Component is released in the space originally occupied
                //  by the component or over an existing component

                if (bounds.contains(0, location.y))
                {
                    if (c == component)
                    {
                        parent.setComponentZOrder(component, originalZOrder);
                    }
                    else
                    {
                        parent.add(component, i);
                    }

                    moved = true;
                    break;
                }
            }
        }

        //  Component is positioned below all components in the container

        if (!moved)
        {
            parent.add(component, parent.getComponentCount() - 1);
        }

        //  Restore layout manager

        parent.setPreferredSize( null );
        parent.setLayout(layout);
        parent.validate();
        parent.repaint();
        component.requestFocusInWindow();
    }

    private static void createAndShowGUI()
    {
        JPanel panel = new JPanel( new GridLayout(0, 1) );
        DragListener drag = new DragListener();

        for (int i = 0; i <10; i++)
        {
            JButton button = new JButton("" + i);
            button.setFont(new java.awt.Font("Tahoma", 1, 48));
            button.setForeground(new java.awt.Color(255, 0, 0));
            button.addMouseListener(drag);
            button.addMouseMotionListener(drag);
            panel.add( button );
        }

        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( new JScrollPane(panel) );
        frame.setLocationByPlatform( true );
        frame.setSize(200, 400);
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
}
person camickr    schedule 25.05.2015
comment
Спасибо, работает прелесть, знал, что я что-то просматривал здесь. Надеюсь, у нас будет возможность переработать это с помощью Transferable, как предлагает @MadProgrammer. - person Java Devil; 25.05.2015
comment
Позор, это не позволяет компоненту занять новое положение ПОСЛЕ того, как вы отпустите кнопку мыши;) - person MadProgrammer; 25.05.2015
comment
@MadProgrammer, добавлено обновление, чтобы компонент мог занять новое положение. - person camickr; 25.05.2015

У вас есть две основные проблемы: первая заключается в том, что вы пытаетесь бороться с менеджером компоновки, который будет перекомпоновывать компоненты, когда он становится недействительным, а вторая заключается в том, что вы очень странным образом выполняете процесс перетаскивания.

Когда видимая область области просмотра изменяется, компонент подвергается повторной проверке, что приводит к пересчету положения компонента, в результате чего он на мгновение возвращается в исходное положение.

Выбор, который у вас есть в данный момент времени, это обойтись без него.

mainPanel.setLayout(null);
mainPanel.setPreferredSize(new Dimension(200, 600));

// Below Repeated for buttons 1-9 (left out for conciseness)
jButton1.setFont(new java.awt.Font("Tahoma", 1, 48)); // NOI18N
jButton1.setForeground(new java.awt.Color(255, 0, 0));
jButton1.setText("1");
jButton1.setBounds(0, 0, 100, 100);
mainPanel.add(jButton1);

Это вызовет проблемы, когда вы введете больше компонентов.

Ваш mouseDragged метод тоже можно серьезно упростить

@Override
public void mouseDragged(MouseEvent me) {
    JComponent source = (JComponent) me.getComponent();
    JComponent parent = (JComponent) source.getParent();

    Point p = me.getPoint();
    p = SwingUtilities.convertPoint(source, p, parent);

    Rectangle bounds = source.getBounds();
    bounds.setLocation(p);  

    bounds.x -= pressed.getX();
    bounds.y -= pressed.getY();
    source.setBounds(bounds);
    parent.scrollRectToVisible(bounds);
}

Лучшим решением было бы использовать Transferable API и/или API Drag'n'Drop, которые уже существуют, в основном удаляя компонент из его текущего контейнера и повторно добавляя его в другую позицию в иерархии компонентов на основе его удаления. место расположения. Это позволяет вам продолжать использовать менеджеры компоновки;)

Например, взгляните на Java: как перетаскивать компоненты JPanel

Обновлено с помощью примера DnD

DragNDrop

Итак, пример заимствован из Java. - Как перетаскивать JPanel с его компонентами, но позволяет вам «перемещать» компоненты, когда они перетаскиваются. В качестве дополнительного бонуса есть хороший «индикатор» того, где компонент «должен» появиться...

Поскольку компонент сериализуется, когда он «экспортируется», это вызывает бесконечные проблемы со слушателями и DragGestureRecognizer. С этой целью я реализовал DragDropManager, единственной целью которого является install и uninstall DragGestureHandler и DragGestureRecognizer в определенных точках процесса перетаскивания... вот почему я склонен передавать состояние, а не компоненты :P

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetContext;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class DragginTest extends javax.swing.JFrame {

    public DragginTest() {
        initComponents();
    }

    @SuppressWarnings("unchecked")

    private void initComponents() {
        jScrollPane1 = new javax.swing.JScrollPane();
        mainPanel = new javax.swing.JPanel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setPreferredSize(new java.awt.Dimension(450, 450));

        mainPanel.setLayout(new GridLayout(10, 0));

        // Below Repeated for buttons 1-9 (left out for conciseness)
        for (int index = 0; index < 10; index++) {
            JButton btn = new JButton(String.valueOf(index));
            btn.setFont(new java.awt.Font("Tahoma", 1, 48)); // NOI18N
            btn.setForeground(new java.awt.Color(255, 0, 0));

            DragDropManager.INSTANCE.installDrag(btn);

            mainPanel.add(btn);
        }
        // End Repeat

        DropHandler dropHandler = new DropHandler();
        DropTarget dropTarget = new DropTarget(mainPanel, DnDConstants.ACTION_MOVE, dropHandler, true);
        mainPanel.setDropTarget(dropTarget);

        jScrollPane1.setViewportView(mainPanel);

        getContentPane().setLayout(new BorderLayout());
        add(jScrollPane1);

        pack();
    }

    public static void main(String args[]) {
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new DragginTest().setVisible(true);
            }
        });
    }

    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JPanel mainPanel;

    public enum DragDropManager {

        INSTANCE;

        private Map<Component, DragManager> handlers = new HashMap<>(25);

        protected void installDrag(Component comp) {
            handlers.put(comp, new DragManager(comp));
        }

        protected void uninstallDrag(Component comp) {
            DragManager manager = handlers.remove(comp);
            if (manager != null) {
                manager.uninstall();
            }
        }

        protected class DragManager {

            DragGestureHandler dragGestureHandler;
            DragGestureRecognizer dgr;

            public DragManager(Component comp) {
                dragGestureHandler = new DragGestureHandler(comp);
                dgr = DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
                                comp,
                                DnDConstants.ACTION_MOVE,
                                dragGestureHandler);
            }

            public void uninstall() {
                dgr.removeDragGestureListener(dragGestureHandler);
                dragGestureHandler = null;
                dgr = null;
            }

        }
    }

    public static class ComponentDataFlavor extends DataFlavor {

        // This saves me having to make lots of copies of the same thing
        public static final ComponentDataFlavor SHARED_INSTANCE = new ComponentDataFlavor();

        public ComponentDataFlavor() {

            super(JPanel.class, null);

        }

    }

    public static class ComponentTransferable implements Transferable {

        private DataFlavor[] flavors = new DataFlavor[]{ComponentDataFlavor.SHARED_INSTANCE};
        private Component component;

        public ComponentTransferable(Component panel) {
            this.component = panel;
        }

        @Override
        public DataFlavor[] getTransferDataFlavors() {
            return flavors;
        }

        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {

            // Okay, for this example, this is over kill, but makes it easier
            // to add new flavor support by subclassing
            boolean supported = false;

            for (DataFlavor mine : getTransferDataFlavors()) {

                if (mine.equals(flavor)) {

                    supported = true;
                    break;

                }

            }

            return supported;

        }

        public Component getComponent() {

            return component;

        }

        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {

            Object data = null;
            if (isDataFlavorSupported(flavor)) {

                data = getComponent();

            } else {

                throw new UnsupportedFlavorException(flavor);

            }

            return data;

        }

    }

    public static class DragGestureHandler implements DragGestureListener, DragSourceListener {

        private Container parent;
        private final Component component;

        public DragGestureHandler(Component child) {

            this.component = child;

        }

        public Component getComponent() {
            return component;
        }

        public void setParent(Container parent) {
            this.parent = parent;
        }

        public Container getParent() {
            return parent;
        }

        @Override
        public void dragGestureRecognized(DragGestureEvent dge) {

            // When the drag begins, we need to grab a reference to the
            // parent container so we can return it if the drop
            // is rejected
            Container parent = getComponent().getParent();

            setParent(parent);

            // Remove the panel from the parent.  If we don't do this, it
            // can cause serialization issues.  We could over come this
            // by allowing the drop target to remove the component, but that's
            // an argument for another day
            parent.remove(getComponent());

            // Update the display
            parent.invalidate();
            parent.repaint();

            // Create our transferable wrapper
            Transferable transferable = new ComponentTransferable(getComponent());

            // Start the "drag" process...
            DragSource ds = dge.getDragSource();
            ds.startDrag(dge, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR), transferable, this);

            DragDropManager.INSTANCE.uninstallDrag(getComponent());

        }

        @Override
        public void dragEnter(DragSourceDragEvent dsde) {
        }

        @Override
        public void dragOver(DragSourceDragEvent dsde) {
        }

        @Override
        public void dropActionChanged(DragSourceDragEvent dsde) {
        }

        @Override
        public void dragExit(DragSourceEvent dse) {
        }

        @Override
        public void dragDropEnd(DragSourceDropEvent dsde) {

            // If the drop was not sucessful, we need to
            // return the component back to it's previous
            // parent
            if (!dsde.getDropSuccess()) {

                getParent().add(getComponent());

                getParent().invalidate();
                getParent().repaint();

            }
        }
    }

    public class DropHandler implements DropTargetListener {

        private JComponent spacer = new JPanel();

        public DropHandler() {
            spacer.setBackground(Color.RED);
        }

        @Override
        public void dragEnter(DropTargetDragEvent dtde) {

            // Determine if can actual process the contents comming in.
            // You could try and inspect the transferable as well, but 
            // There is an issue on the MacOS under some circumstances
            // where it does not actually bundle the data until you accept the
            // drop.
            if (dtde.isDataFlavorSupported(ComponentDataFlavor.SHARED_INSTANCE)) {

                dtde.acceptDrag(DnDConstants.ACTION_MOVE);

            } else {

                dtde.rejectDrag();

            }

        }

        @Override
        public void dragOver(DropTargetDragEvent dtde) {

            if (dtde.isDataFlavorSupported(ComponentDataFlavor.SHARED_INSTANCE)) {
                Point p = dtde.getLocation();
                DropTargetContext dtc = dtde.getDropTargetContext();
                Container parent = (Container) dtc.getComponent();
                Component target = parent.getComponentAt(p);
                int insertPoint = Math.max(0, parent.getComponentZOrder(target));
                if (spacer.getParent() == null) {
                    parent.add(spacer, insertPoint);
                } else {
                    parent.setComponentZOrder(spacer, insertPoint);
                }
                parent.revalidate();
                parent.repaint();

                Point pic = SwingUtilities.convertPoint(spacer, p, target);
                Rectangle bounds = spacer.getBounds();
                bounds.setLocation(pic);

                ((JComponent) parent).scrollRectToVisible(bounds);
            }
        }

        @Override
        public void dropActionChanged(DropTargetDragEvent dtde) {
        }

        @Override
        public void dragExit(DropTargetEvent dte) {
            Container parent = (Container) dte.getDropTargetContext().getComponent();
            parent.remove(spacer);
            parent.revalidate();
            parent.repaint();
        }

        @Override
        public void drop(DropTargetDropEvent dtde) {

            boolean success = false;

            // Basically, we want to unwrap the present...
            if (dtde.isDataFlavorSupported(ComponentDataFlavor.SHARED_INSTANCE)) {

                Transferable transferable = dtde.getTransferable();
                try {

                    Object data = transferable.getTransferData(ComponentDataFlavor.SHARED_INSTANCE);
                    if (data instanceof Component) {

                        Component target = (Component) data;

                        DropTargetContext dtc = dtde.getDropTargetContext();
                        Component component = dtc.getComponent();

                        if (component instanceof JComponent) {

                            Container parent = target.getParent();
                            if (parent != null) {

                                parent.remove(target);

                            }
                            parent = (Container) component;

                            Point p = dtde.getLocation();
                            Component before = parent.getComponentAt(p);
                            int insertPoint = Math.max(0, parent.getComponentZOrder(before));
                            parent.remove(spacer);
                            System.out.println(insertPoint);
                            parent.add(target, insertPoint);
                            parent.revalidate();
                            parent.repaint();

                            DragDropManager.INSTANCE.installDrag(target);

                            success = true;
                            dtde.acceptDrop(DnDConstants.ACTION_MOVE);

                            invalidate();
                            repaint();

                        } else {

                            success = false;
                            dtde.rejectDrop();

                        }

                    } else {

                        success = false;
                        dtde.rejectDrop();

                    }

                } catch (Exception exp) {

                    success = false;
                    dtde.rejectDrop();
                    exp.printStackTrace();

                }

            } else {

                success = false;
                dtde.rejectDrop();

            }

            dtde.dropComplete(success);

        }

    }
}
person MadProgrammer    schedule 25.05.2015
comment
Да, менеджер компоновки временно сбрасывает местоположение во время процесса перетаскивания (+1). Хотя вы отказались от вопроса, поэтому я добавил свои два цента в свой ответ :) - person camickr; 25.05.2015
comment
Да, я пытаюсь разработать переносимое решение;) - person MadProgrammer; 25.05.2015
comment
В идеале я хочу использовать решение Transferable, однако из-за нехватки времени прямо сейчас у меня не было времени, чтобы полностью переработать это, но я обязательно посмотрю на это в будущем. - person Java Devil; 25.05.2015
comment
Приятно :) Однако есть несколько особенностей, в основном это то, что прокрутка не очень плавная - очень быстрая и прерывистая, курсор мерцает при удерживании перетаскивания, также вы не получаете изображение плавающего компонента при движении. Хотя, наверное, это все из-за мелких доработок. Большое спасибо, что собрали это вместе :) - person Java Devil; 25.05.2015
comment
Добавление изображения перетаскивания на самом деле является полной головной болью в .... коде. Я бы подумал о создании BufferedImage кнопки и использовании компонента spacer для ее рисования. Прокрутка так же хороша, как и при использовании scrollToVisibleRect :P - person MadProgrammer; 25.05.2015