34

如何使用 Java Swing 实现可拖动选项卡?而不是静态的 JTabbedPane 我想将选项卡拖放到不同的位置以重新排列选项卡。

编辑Java 教程 - 拖放和数据传输

4

5 回答 5

30

诅咒!被谷歌搜索打败了。不幸的是,确实没有简单的方法可以在 Swing 中创建可拖动的选项卡窗格(或任何其他组件)。因此,虽然上面的例子已经完成,但我刚刚写的这个例子要简单一些。因此,它有望更清楚地展示所涉及的更先进的技术。步骤是:

  1. 检测到发生了拖拽
  2. 将拖动的选项卡绘制到屏幕外缓冲区
  3. 拖动时跟踪鼠标位置
  4. 在组件顶部的缓冲区中绘制选项卡。

上面的示例将为您提供所需的内容,但如果您想真正了解此处应用的技术,则修整此示例的边缘并向其添加上面演示的额外功能可能是一个更好的练习。

或者,也许我只是感到失望,因为我花时间编写了这个已经存在的解决方案:p

import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTabbedPane;


public class DraggableTabbedPane extends JTabbedPane {

  private boolean dragging = false;
  private Image tabImage = null;
  private Point currentMouseLocation = null;
  private int draggedTabIndex = 0;

  public DraggableTabbedPane() {
    super();
    addMouseMotionListener(new MouseMotionAdapter() {
      public void mouseDragged(MouseEvent e) {

        if(!dragging) {
          // Gets the tab index based on the mouse position
          int tabNumber = getUI().tabForCoordinate(DraggableTabbedPane.this, e.getX(), e.getY());

          if(tabNumber >= 0) {
            draggedTabIndex = tabNumber;
            Rectangle bounds = getUI().getTabBounds(DraggableTabbedPane.this, tabNumber);


            // Paint the tabbed pane to a buffer
            Image totalImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
            Graphics totalGraphics = totalImage.getGraphics();
            totalGraphics.setClip(bounds);
            // Don't be double buffered when painting to a static image.
            setDoubleBuffered(false);
            paintComponent(totalGraphics);

            // Paint just the dragged tab to the buffer
            tabImage = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB);
            Graphics graphics = tabImage.getGraphics();
            graphics.drawImage(totalImage, 0, 0, bounds.width, bounds.height, bounds.x, bounds.y, bounds.x + bounds.width, bounds.y+bounds.height, DraggableTabbedPane.this);

            dragging = true;
            repaint();
          }
        } else {
          currentMouseLocation = e.getPoint();

          // Need to repaint
          repaint();
        }

        super.mouseDragged(e);
      }
    });

    addMouseListener(new MouseAdapter() {
      public void mouseReleased(MouseEvent e) {

        if(dragging) {
          int tabNumber = getUI().tabForCoordinate(DraggableTabbedPane.this, e.getX(), 10);

          if(tabNumber >= 0) {
            Component comp = getComponentAt(draggedTabIndex);
            String title = getTitleAt(draggedTabIndex);
            removeTabAt(draggedTabIndex);
            insertTab(title, null, comp, null, tabNumber);
          }
        }

        dragging = false;
        tabImage = null;
      }
    });
  }

  protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    // Are we dragging?
    if(dragging && currentMouseLocation != null && tabImage != null) {
      // Draw the dragged tab
      g.drawImage(tabImage, currentMouseLocation.x, currentMouseLocation.y, this);
    }
  }

  public static void main(String[] args) {
    JFrame test = new JFrame("Tab test");
    test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    test.setSize(400, 400);

    DraggableTabbedPane tabs = new DraggableTabbedPane();
    tabs.addTab("One", new JButton("One"));
    tabs.addTab("Two", new JButton("Two"));
    tabs.addTab("Three", new JButton("Three"));
    tabs.addTab("Four", new JButton("Four"));

    test.add(tabs);
    test.setVisible(true);
  }
}
于 2008-09-13T03:33:48.240 回答
14

我喜欢Terai Atsuhiro san 的 DnDTabbedPane,但我想从中获得更多。最初的 Terai 实现在 TabbedPane 中传输选项卡,但如果我可以从一个 TabbedPane 拖动到另一个会更好。

受到@Tom努力的启发,我决定自己修改代码。我添加了一些细节。例如,幻影选项卡现在沿着选项卡式窗格滑动,而不是与鼠标一起移动。

setAcceptor(TabAcceptor a_acceptor)应该让消费者代码决定是否让一个选项卡从一个选项卡式窗格转移到另一个。默认接受器总是返回true

/** Modified DnDTabbedPane.java
 * http://java-swing-tips.blogspot.com/2008/04/drag-and-drop-tabs-in-jtabbedpane.html
 * originally written by Terai Atsuhiro.
 * so that tabs can be transfered from one pane to another.
 * eed3si9n.
 */

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.swing.*;

public class DnDTabbedPane extends JTabbedPane {
    public static final long serialVersionUID = 1L;
    private static final int LINEWIDTH = 3;
    private static final String NAME = "TabTransferData";
    private final DataFlavor FLAVOR = new DataFlavor(
            DataFlavor.javaJVMLocalObjectMimeType, NAME);
    private static GhostGlassPane s_glassPane = new GhostGlassPane();

    private boolean m_isDrawRect = false;
    private final Rectangle2D m_lineRect = new Rectangle2D.Double();

    private final Color m_lineColor = new Color(0, 100, 255);
    private TabAcceptor m_acceptor = null;

    public DnDTabbedPane() {
        super();
        final DragSourceListener dsl = new DragSourceListener() {
            public void dragEnter(DragSourceDragEvent e) {
                e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
            }

            public void dragExit(DragSourceEvent e) {
                e.getDragSourceContext()
                        .setCursor(DragSource.DefaultMoveNoDrop);
                m_lineRect.setRect(0, 0, 0, 0);
                m_isDrawRect = false;
                s_glassPane.setPoint(new Point(-1000, -1000));
                s_glassPane.repaint();
            }

            public void dragOver(DragSourceDragEvent e) {
                //e.getLocation()
                //This method returns a Point indicating the cursor location in screen coordinates at the moment

                TabTransferData data = getTabTransferData(e);
                if (data == null) {
                    e.getDragSourceContext().setCursor(
                            DragSource.DefaultMoveNoDrop);
                    return;
                } // if

                /*
                Point tabPt = e.getLocation();
                SwingUtilities.convertPointFromScreen(tabPt, DnDTabbedPane.this);
                if (DnDTabbedPane.this.contains(tabPt)) {
                    int targetIdx = getTargetTabIndex(tabPt);
                    int sourceIndex = data.getTabIndex();
                    if (getTabAreaBound().contains(tabPt)
                            && (targetIdx >= 0)
                            && (targetIdx != sourceIndex)
                            && (targetIdx != sourceIndex + 1)) {
                        e.getDragSourceContext().setCursor(
                                DragSource.DefaultMoveDrop);

                        return;
                    } // if

                    e.getDragSourceContext().setCursor(
                            DragSource.DefaultMoveNoDrop);
                    return;
                } // if
                */

                e.getDragSourceContext().setCursor(
                        DragSource.DefaultMoveDrop);
            }

            public void dragDropEnd(DragSourceDropEvent e) {
                m_isDrawRect = false;
                m_lineRect.setRect(0, 0, 0, 0);
                // m_dragTabIndex = -1;

                if (hasGhost()) {
                    s_glassPane.setVisible(false);
                    s_glassPane.setImage(null);
                }
            }

            public void dropActionChanged(DragSourceDragEvent e) {
            }
        };

        final DragGestureListener dgl = new DragGestureListener() {
            public void dragGestureRecognized(DragGestureEvent e) {
                // System.out.println("dragGestureRecognized");

                Point tabPt = e.getDragOrigin();
                int dragTabIndex = indexAtLocation(tabPt.x, tabPt.y);
                if (dragTabIndex < 0) {
                    return;
                } // if

                initGlassPane(e.getComponent(), e.getDragOrigin(), dragTabIndex);
                try {
                    e.startDrag(DragSource.DefaultMoveDrop, 
                            new TabTransferable(DnDTabbedPane.this, dragTabIndex), dsl);
                } catch (InvalidDnDOperationException idoe) {
                    idoe.printStackTrace();
                }
            }
        };

        //dropTarget =
        new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE,
                new CDropTargetListener(), true);
        new DragSource().createDefaultDragGestureRecognizer(this,
                DnDConstants.ACTION_COPY_OR_MOVE, dgl);
        m_acceptor = new TabAcceptor() {
            public boolean isDropAcceptable(DnDTabbedPane a_component, int a_index) {
                return true;
            }
        };
    }

    public TabAcceptor getAcceptor() {
        return m_acceptor;
    }

    public void setAcceptor(TabAcceptor a_value) {
        m_acceptor = a_value;
    }

    private TabTransferData getTabTransferData(DropTargetDropEvent a_event) {       
        try {
            TabTransferData data = (TabTransferData) a_event.getTransferable().getTransferData(FLAVOR);             
            return data;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    private TabTransferData getTabTransferData(DropTargetDragEvent a_event) {
        try {
            TabTransferData data = (TabTransferData) a_event.getTransferable().getTransferData(FLAVOR);             
            return data;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    private TabTransferData getTabTransferData(DragSourceDragEvent a_event) {
        try {
            TabTransferData data = (TabTransferData) a_event.getDragSourceContext()
                .getTransferable().getTransferData(FLAVOR);             
            return data;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;        
    }

    class TabTransferable implements Transferable {
        private TabTransferData m_data = null;

        public TabTransferable(DnDTabbedPane a_tabbedPane, int a_tabIndex) {
            m_data = new TabTransferData(DnDTabbedPane.this, a_tabIndex);
        }

        public Object getTransferData(DataFlavor flavor) {
            return m_data;
            // return DnDTabbedPane.this;
        }

        public DataFlavor[] getTransferDataFlavors() {
            DataFlavor[] f = new DataFlavor[1];
            f[0] = FLAVOR;
            return f;
        }

        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return flavor.getHumanPresentableName().equals(NAME);
        }       
    }

    class TabTransferData {
        private DnDTabbedPane m_tabbedPane = null;
        private int m_tabIndex = -1;

        public TabTransferData() {
        }

        public TabTransferData(DnDTabbedPane a_tabbedPane, int a_tabIndex) {
            m_tabbedPane = a_tabbedPane;
            m_tabIndex = a_tabIndex;
        }

        public DnDTabbedPane getTabbedPane() {
            return m_tabbedPane;
        }

        public void setTabbedPane(DnDTabbedPane pane) {
            m_tabbedPane = pane;
        }

        public int getTabIndex() {
            return m_tabIndex;
        }

        public void setTabIndex(int index) {
            m_tabIndex = index;
        }
    }

    private Point buildGhostLocation(Point a_location) {
        Point retval = new Point(a_location);

        switch (getTabPlacement()) {
            case JTabbedPane.TOP: {
                retval.y = 1;
                retval.x -= s_glassPane.getGhostWidth() / 2;
            } break;

            case JTabbedPane.BOTTOM: {
                retval.y = getHeight() - 1 - s_glassPane.getGhostHeight();
                retval.x -= s_glassPane.getGhostWidth() / 2;
            } break;

            case JTabbedPane.LEFT: {
                retval.x = 1;
                retval.y -= s_glassPane.getGhostHeight() / 2;
            } break;

            case JTabbedPane.RIGHT: {
                retval.x = getWidth() - 1 - s_glassPane.getGhostWidth();
                retval.y -= s_glassPane.getGhostHeight() / 2;
            } break;
        } // switch

        retval = SwingUtilities.convertPoint(DnDTabbedPane.this,
                retval, s_glassPane);
        return retval;
    }

    class CDropTargetListener implements DropTargetListener {
        public void dragEnter(DropTargetDragEvent e) {
            // System.out.println("DropTarget.dragEnter: " + DnDTabbedPane.this);

            if (isDragAcceptable(e)) {
                e.acceptDrag(e.getDropAction());
            } else {
                e.rejectDrag();
            } // if
        }

        public void dragExit(DropTargetEvent e) {
            // System.out.println("DropTarget.dragExit: " + DnDTabbedPane.this);
            m_isDrawRect = false;
        }

        public void dropActionChanged(DropTargetDragEvent e) {
        }

        public void dragOver(final DropTargetDragEvent e) {
            TabTransferData data = getTabTransferData(e);

            if (getTabPlacement() == JTabbedPane.TOP
                    || getTabPlacement() == JTabbedPane.BOTTOM) {
                initTargetLeftRightLine(getTargetTabIndex(e.getLocation()), data);
            } else {
                initTargetTopBottomLine(getTargetTabIndex(e.getLocation()), data);
            } // if-else

            repaint();
            if (hasGhost()) {
                s_glassPane.setPoint(buildGhostLocation(e.getLocation()));
                s_glassPane.repaint();
            }
        }

        public void drop(DropTargetDropEvent a_event) {
            // System.out.println("DropTarget.drop: " + DnDTabbedPane.this);

            if (isDropAcceptable(a_event)) {
                convertTab(getTabTransferData(a_event),
                    getTargetTabIndex(a_event.getLocation()));
                a_event.dropComplete(true);
            } else {
                a_event.dropComplete(false);
            } // if-else

            m_isDrawRect = false;
            repaint();
        }

        public boolean isDragAcceptable(DropTargetDragEvent e) {
            Transferable t = e.getTransferable();
            if (t == null) {
                return false;
            } // if

            DataFlavor[] flavor = e.getCurrentDataFlavors();
            if (!t.isDataFlavorSupported(flavor[0])) {
                return false;
            } // if

            TabTransferData data = getTabTransferData(e);

            if (DnDTabbedPane.this == data.getTabbedPane()
                    && data.getTabIndex() >= 0) {
                return true;
            } // if

            if (DnDTabbedPane.this != data.getTabbedPane()) {
                if (m_acceptor != null) {
                    return m_acceptor.isDropAcceptable(data.getTabbedPane(), data.getTabIndex());
                } // if
            } // if

            return false;
        }

        public boolean isDropAcceptable(DropTargetDropEvent e) {
            Transferable t = e.getTransferable();
            if (t == null) {
                return false;
            } // if

            DataFlavor[] flavor = e.getCurrentDataFlavors();
            if (!t.isDataFlavorSupported(flavor[0])) {
                return false;
            } // if

            TabTransferData data = getTabTransferData(e);

            if (DnDTabbedPane.this == data.getTabbedPane()
                    && data.getTabIndex() >= 0) {
                return true;
            } // if

            if (DnDTabbedPane.this != data.getTabbedPane()) {
                if (m_acceptor != null) {
                    return m_acceptor.isDropAcceptable(data.getTabbedPane(), data.getTabIndex());
                } // if
            } // if

            return false;
        }
    }

    private boolean m_hasGhost = true;

    public void setPaintGhost(boolean flag) {
        m_hasGhost = flag;
    }

    public boolean hasGhost() {
        return m_hasGhost;
    }

    /**
     * returns potential index for drop.
     * @param a_point point given in the drop site component's coordinate
     * @return returns potential index for drop.
     */
    private int getTargetTabIndex(Point a_point) {
        boolean isTopOrBottom = getTabPlacement() == JTabbedPane.TOP
                || getTabPlacement() == JTabbedPane.BOTTOM;

        // if the pane is empty, the target index is always zero.
        if (getTabCount() == 0) {
            return 0;
        } // if

        for (int i = 0; i < getTabCount(); i++) {
            Rectangle r = getBoundsAt(i);
            if (isTopOrBottom) {
                r.setRect(r.x - r.width / 2, r.y, r.width, r.height);
            } else {
                r.setRect(r.x, r.y - r.height / 2, r.width, r.height);
            } // if-else

            if (r.contains(a_point)) {
                return i;
            } // if
        } // for

        Rectangle r = getBoundsAt(getTabCount() - 1);
        if (isTopOrBottom) {
            int x = r.x + r.width / 2;
            r.setRect(x, r.y, getWidth() - x, r.height);
        } else {
            int y = r.y + r.height / 2;
            r.setRect(r.x, y, r.width, getHeight() - y);
        } // if-else

        return r.contains(a_point) ? getTabCount() : -1;
    }

    private void convertTab(TabTransferData a_data, int a_targetIndex) {
        DnDTabbedPane source = a_data.getTabbedPane();
        int sourceIndex = a_data.getTabIndex();
        if (sourceIndex < 0) {
            return;
        } // if

        Component cmp = source.getComponentAt(sourceIndex);
        String str = source.getTitleAt(sourceIndex);
        if (this != source) {
            source.remove(sourceIndex);

            if (a_targetIndex == getTabCount()) {
                addTab(str, cmp);
            } else {
                if (a_targetIndex < 0) {
                    a_targetIndex = 0;
                } // if

                insertTab(str, null, cmp, null, a_targetIndex);

            } // if

            setSelectedComponent(cmp);
            // System.out.println("press="+sourceIndex+" next="+a_targetIndex);
            return;
        } // if

        if (a_targetIndex < 0 || sourceIndex == a_targetIndex) {
            //System.out.println("press="+prev+" next="+next);
            return;
        } // if

        if (a_targetIndex == getTabCount()) {
            //System.out.println("last: press="+prev+" next="+next);
            source.remove(sourceIndex);
            addTab(str, cmp);
            setSelectedIndex(getTabCount() - 1);
        } else if (sourceIndex > a_targetIndex) {
            //System.out.println("   >: press="+prev+" next="+next);
            source.remove(sourceIndex);
            insertTab(str, null, cmp, null, a_targetIndex);
            setSelectedIndex(a_targetIndex);
        } else {
            //System.out.println("   <: press="+prev+" next="+next);
            source.remove(sourceIndex);
            insertTab(str, null, cmp, null, a_targetIndex - 1);
            setSelectedIndex(a_targetIndex - 1);
        }
    }

    private void initTargetLeftRightLine(int next, TabTransferData a_data) {        
        if (next < 0) {
            m_lineRect.setRect(0, 0, 0, 0);
            m_isDrawRect = false;
            return;
        } // if

        if ((a_data.getTabbedPane() == this)
                && (a_data.getTabIndex() == next
                || next - a_data.getTabIndex() == 1)) {
            m_lineRect.setRect(0, 0, 0, 0);
            m_isDrawRect = false;
        } else if (getTabCount() == 0) {
            m_lineRect.setRect(0, 0, 0, 0);
            m_isDrawRect = false;
            return;
        } else if (next == 0) {
            Rectangle rect = getBoundsAt(0);
            m_lineRect.setRect(-LINEWIDTH / 2, rect.y, LINEWIDTH, rect.height);
            m_isDrawRect = true;
        } else if (next == getTabCount()) {
            Rectangle rect = getBoundsAt(getTabCount() - 1);
            m_lineRect.setRect(rect.x + rect.width - LINEWIDTH / 2, rect.y,
                    LINEWIDTH, rect.height);
            m_isDrawRect = true;
        } else {
            Rectangle rect = getBoundsAt(next - 1);
            m_lineRect.setRect(rect.x + rect.width - LINEWIDTH / 2, rect.y,
                    LINEWIDTH, rect.height);
            m_isDrawRect = true;
        }
    }

    private void initTargetTopBottomLine(int next, TabTransferData a_data) {
        if (next < 0) {
            m_lineRect.setRect(0, 0, 0, 0);
            m_isDrawRect = false;
            return;
        } // if

        if ((a_data.getTabbedPane() == this)
                && (a_data.getTabIndex() == next
                || next - a_data.getTabIndex() == 1)) {
            m_lineRect.setRect(0, 0, 0, 0);
            m_isDrawRect = false;
        } else if (getTabCount() == 0) {
            m_lineRect.setRect(0, 0, 0, 0);
            m_isDrawRect = false;
            return;
        } else if (next == getTabCount()) {
            Rectangle rect = getBoundsAt(getTabCount() - 1);
            m_lineRect.setRect(rect.x, rect.y + rect.height - LINEWIDTH / 2,
                    rect.width, LINEWIDTH);
            m_isDrawRect = true;
        } else if (next == 0) {
            Rectangle rect = getBoundsAt(0);
            m_lineRect.setRect(rect.x, -LINEWIDTH / 2, rect.width, LINEWIDTH);
            m_isDrawRect = true;
        } else {
            Rectangle rect = getBoundsAt(next - 1);
            m_lineRect.setRect(rect.x, rect.y + rect.height - LINEWIDTH / 2,
                    rect.width, LINEWIDTH);
            m_isDrawRect = true;
        }
    }

    private void initGlassPane(Component c, Point tabPt, int a_tabIndex) {
        //Point p = (Point) pt.clone();
        getRootPane().setGlassPane(s_glassPane);
        if (hasGhost()) {
            Rectangle rect = getBoundsAt(a_tabIndex);
            BufferedImage image = new BufferedImage(c.getWidth(),
                    c.getHeight(), BufferedImage.TYPE_INT_ARGB);
            Graphics g = image.getGraphics();
            c.paint(g);
            image = image.getSubimage(rect.x, rect.y, rect.width, rect.height);
            s_glassPane.setImage(image);            
        } // if

        s_glassPane.setPoint(buildGhostLocation(tabPt));
        s_glassPane.setVisible(true);
    }

    private Rectangle getTabAreaBound() {
        Rectangle lastTab = getUI().getTabBounds(this, getTabCount() - 1);
        return new Rectangle(0, 0, getWidth(), lastTab.y + lastTab.height);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        if (m_isDrawRect) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setPaint(m_lineColor);
            g2.fill(m_lineRect);
        } // if
    }

    public interface TabAcceptor {
        boolean isDropAcceptable(DnDTabbedPane a_component, int a_index);
    }
}

class GhostGlassPane extends JPanel {
    public static final long serialVersionUID = 1L;
    private final AlphaComposite m_composite;

    private Point m_location = new Point(0, 0);

    private BufferedImage m_draggingGhost = null;

    public GhostGlassPane() {
        setOpaque(false);
        m_composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f);
    }

    public void setImage(BufferedImage draggingGhost) {
        m_draggingGhost = draggingGhost;
    }

    public void setPoint(Point a_location) {
        m_location.x = a_location.x;
        m_location.y = a_location.y;
    }

    public int getGhostWidth() {
        if (m_draggingGhost == null) {
            return 0;
        } // if

        return m_draggingGhost.getWidth(this);
    }

    public int getGhostHeight() {
        if (m_draggingGhost == null) {
            return 0;
        } // if

        return m_draggingGhost.getHeight(this);
    }

    public void paintComponent(Graphics g) {
        if (m_draggingGhost == null) {
            return;
        } // if 

        Graphics2D g2 = (Graphics2D) g;
        g2.setComposite(m_composite);

        g2.drawImage(m_draggingGhost, (int) m_location.getX(), (int) m_location.getY(), null);
    }
}
于 2008-09-15T07:41:08.797 回答
7

在管子上找到了这段代码:

class DnDTabbedPane extends JTabbedPane {
  private static final int LINEWIDTH = 3;
  private static final String NAME = "test";
  private final GhostGlassPane glassPane = new GhostGlassPane();
  private final Rectangle2D lineRect   = new Rectangle2D.Double();
  private final Color     lineColor  = new Color(0, 100, 255);
  //private final DragSource dragSource  = new DragSource();
  //private final DropTarget dropTarget;
  private int dragTabIndex = -1;

  public DnDTabbedPane() {
    super();
    final DragSourceListener dsl = new DragSourceListener() {
      public void dragEnter(DragSourceDragEvent e) {
        e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
      }
      public void dragExit(DragSourceEvent e) {
        e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
        lineRect.setRect(0,0,0,0);
        glassPane.setPoint(new Point(-1000,-1000));
        glassPane.repaint();
      }
      public void dragOver(DragSourceDragEvent e) {
        //e.getLocation()
        //This method returns a Point indicating the cursor location in screen coordinates at the moment
        Point tabPt = e.getLocation();
        SwingUtilities.convertPointFromScreen(tabPt, DnDTabbedPane.this);
        Point glassPt = e.getLocation();
        SwingUtilities.convertPointFromScreen(glassPt, glassPane);
        int targetIdx = getTargetTabIndex(glassPt);
        if(getTabAreaBound().contains(tabPt) && targetIdx>=0 &&
           targetIdx!=dragTabIndex && targetIdx!=dragTabIndex+1) {
          e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
        }else{
          e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
        }
      }
      public void dragDropEnd(DragSourceDropEvent e) {
        lineRect.setRect(0,0,0,0);
        dragTabIndex = -1;
        if(hasGhost()) {
          glassPane.setVisible(false);
          glassPane.setImage(null);
        }
      }
      public void dropActionChanged(DragSourceDragEvent e) {}
    };
    final Transferable t = new Transferable() {
      private final DataFlavor FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, NAME);
      public Object getTransferData(DataFlavor flavor) {
        return DnDTabbedPane.this;
      }
      public DataFlavor[] getTransferDataFlavors() {
        DataFlavor[] f = new DataFlavor[1];
        f[0] = this.FLAVOR;
        return f;
      }
      public boolean isDataFlavorSupported(DataFlavor flavor) {
        return flavor.getHumanPresentableName().equals(NAME);
      }
    };
    final DragGestureListener dgl = new DragGestureListener() {
      public void dragGestureRecognized(DragGestureEvent e) {
        Point tabPt = e.getDragOrigin();
        dragTabIndex = indexAtLocation(tabPt.x, tabPt.y);
        if(dragTabIndex<0) return;
        initGlassPane(e.getComponent(), e.getDragOrigin());
        try{
          e.startDrag(DragSource.DefaultMoveDrop, t, dsl);
        }catch(InvalidDnDOperationException idoe) {
          idoe.printStackTrace();
        }
      }
    };
    //dropTarget =
    new DropTarget(glassPane, DnDConstants.ACTION_COPY_OR_MOVE, new CDropTargetListener(), true);
    new DragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
  }

  class CDropTargetListener implements DropTargetListener{
    public void dragEnter(DropTargetDragEvent e) {
      if(isDragAcceptable(e)) e.acceptDrag(e.getDropAction());
      else e.rejectDrag();
    }
    public void dragExit(DropTargetEvent e) {}
    public void dropActionChanged(DropTargetDragEvent e) {}
    public void dragOver(final DropTargetDragEvent e) {
      if(getTabPlacement()==JTabbedPane.TOP || getTabPlacement()==JTabbedPane.BOTTOM) {
        initTargetLeftRightLine(getTargetTabIndex(e.getLocation()));
      }else{
        initTargetTopBottomLine(getTargetTabIndex(e.getLocation()));
      }
      repaint();
      if(hasGhost()) {
        glassPane.setPoint(e.getLocation());
        glassPane.repaint();
      }
    }

    public void drop(DropTargetDropEvent e) {
      if(isDropAcceptable(e)) {
        convertTab(dragTabIndex, getTargetTabIndex(e.getLocation()));
        e.dropComplete(true);
      }else{
        e.dropComplete(false);
      }
      repaint();
    }
    public boolean isDragAcceptable(DropTargetDragEvent e) {
      Transferable t = e.getTransferable();
      if(t==null) return false;
      DataFlavor[] f = e.getCurrentDataFlavors();
      if(t.isDataFlavorSupported(f[0]) && dragTabIndex>=0) {
        return true;
      }
      return false;
    }
    public boolean isDropAcceptable(DropTargetDropEvent e) {
      Transferable t = e.getTransferable();
      if(t==null) return false;
      DataFlavor[] f = t.getTransferDataFlavors();
      if(t.isDataFlavorSupported(f[0]) && dragTabIndex>=0) {
        return true;
      }
      return false;
    }
  }

  private boolean hasGhost = true;
  public void setPaintGhost(boolean flag) {
    hasGhost = flag;
  }
  public boolean hasGhost() {
    return hasGhost;
  }
  private int getTargetTabIndex(Point glassPt) {
    Point tabPt = SwingUtilities.convertPoint(glassPane, glassPt, DnDTabbedPane.this);
    boolean isTB = getTabPlacement()==JTabbedPane.TOP || getTabPlacement()==JTabbedPane.BOTTOM;
    for(int i=0;i<getTabCount();i++) {
      Rectangle r = getBoundsAt(i);
      if(isTB) r.setRect(r.x-r.width/2, r.y,  r.width, r.height);
      else   r.setRect(r.x, r.y-r.height/2, r.width, r.height);
      if(r.contains(tabPt)) return i;
    }
    Rectangle r = getBoundsAt(getTabCount()-1);
    if(isTB) r.setRect(r.x+r.width/2, r.y,  r.width, r.height);
    else   r.setRect(r.x, r.y+r.height/2, r.width, r.height);
    return   r.contains(tabPt)?getTabCount():-1;
  }
  private void convertTab(int prev, int next) {
    if(next<0 || prev==next) {
      //System.out.println("press="+prev+" next="+next);
      return;
    }
    Component cmp = getComponentAt(prev);
    String str = getTitleAt(prev);
    if(next==getTabCount()) {
      //System.out.println("last: press="+prev+" next="+next);
      remove(prev);
      addTab(str, cmp);
      setSelectedIndex(getTabCount()-1);
    }else if(prev>next) {
      //System.out.println("   >: press="+prev+" next="+next);
      remove(prev);
      insertTab(str, null, cmp, null, next);
      setSelectedIndex(next);
    }else{
      //System.out.println("   <: press="+prev+" next="+next);
      remove(prev);
      insertTab(str, null, cmp, null, next-1);
      setSelectedIndex(next-1);
    }
  }

  private void initTargetLeftRightLine(int next) {
    if(next<0 || dragTabIndex==next || next-dragTabIndex==1) {
      lineRect.setRect(0,0,0,0);
    }else if(next==getTabCount()) {
      Rectangle rect = getBoundsAt(getTabCount()-1);
      lineRect.setRect(rect.x+rect.width-LINEWIDTH/2,rect.y,LINEWIDTH,rect.height);
    }else if(next==0) {
      Rectangle rect = getBoundsAt(0);
      lineRect.setRect(-LINEWIDTH/2,rect.y,LINEWIDTH,rect.height);
    }else{
      Rectangle rect = getBoundsAt(next-1);
      lineRect.setRect(rect.x+rect.width-LINEWIDTH/2,rect.y,LINEWIDTH,rect.height);
    }
  }
  private void initTargetTopBottomLine(int next) {
    if(next<0 || dragTabIndex==next || next-dragTabIndex==1) {
      lineRect.setRect(0,0,0,0);
    }else if(next==getTabCount()) {
      Rectangle rect = getBoundsAt(getTabCount()-1);
      lineRect.setRect(rect.x,rect.y+rect.height-LINEWIDTH/2,rect.width,LINEWIDTH);
    }else if(next==0) {
      Rectangle rect = getBoundsAt(0);
      lineRect.setRect(rect.x,-LINEWIDTH/2,rect.width,LINEWIDTH);
    }else{
      Rectangle rect = getBoundsAt(next-1);
      lineRect.setRect(rect.x,rect.y+rect.height-LINEWIDTH/2,rect.width,LINEWIDTH);
    }
  }

  private void initGlassPane(Component c, Point tabPt) {
    //Point p = (Point) pt.clone();
    getRootPane().setGlassPane(glassPane);
    if(hasGhost()) {
      Rectangle rect = getBoundsAt(dragTabIndex);
      BufferedImage image = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_ARGB);
      Graphics g = image.getGraphics();
      c.paint(g);
      image = image.getSubimage(rect.x,rect.y,rect.width,rect.height);
      glassPane.setImage(image);
    }
    Point glassPt = SwingUtilities.convertPoint(c, tabPt, glassPane);
    glassPane.setPoint(glassPt);
    glassPane.setVisible(true);
  }

  private Rectangle getTabAreaBound() {
    Rectangle lastTab  = getUI().getTabBounds(this, getTabCount()-1);
    return new Rectangle(0,0,getWidth(),lastTab.y+lastTab.height);
  }

  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    if(dragTabIndex>=0) {
      Graphics2D g2 = (Graphics2D)g;
      g2.setPaint(lineColor);
      g2.fill(lineRect);
    }
  }
}

class GhostGlassPane extends JPanel {
  private final AlphaComposite composite;
  private Point location = new Point(0, 0);
  private BufferedImage draggingGhost = null;
  public GhostGlassPane() {
    setOpaque(false);
    composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
  }
  public void setImage(BufferedImage draggingGhost) {
    this.draggingGhost = draggingGhost;
  }
  public void setPoint(Point location) {
    this.location = location;
  }
  public void paintComponent(Graphics g) {
    if(draggingGhost == null) return;
    Graphics2D g2 = (Graphics2D) g;
    g2.setComposite(composite);
    double xx = location.getX() - (draggingGhost.getWidth(this) /2d);
    double yy = location.getY() - (draggingGhost.getHeight(this)/2d);
    g2.drawImage(draggingGhost, (int)xx, (int)yy , null);
  }
}
于 2008-09-13T02:49:25.507 回答
7

@Tony:看起来Euguenes 解决方案只是忽略了在交换期间保留 TabComponents。

convertTab 方法只需要记住 TabComponent 并将其设置为它创建的新选项卡。

尝试使用这个:

    private void convertTab(TabTransferData a_data, int a_targetIndex) {

            DnDTabbedPane source = a_data.getTabbedPane();
            System.out.println("this=source? " + (this == source));
            int sourceIndex = a_data.getTabIndex();
            if (sourceIndex < 0) {
                    return;
            } // if
            //Save the tab's component, title, and TabComponent.
            Component cmp = source.getComponentAt(sourceIndex);
            String str = source.getTitleAt(sourceIndex);
            Component tcmp = source.getTabComponentAt(sourceIndex);

            if (this != source) {
                    source.remove(sourceIndex);

                    if (a_targetIndex == getTabCount()) {
                            addTab(str, cmp);
                            setTabComponentAt(getTabCount()-1, tcmp);
                    } else {
                            if (a_targetIndex < 0) {
                                    a_targetIndex = 0;
                            } // if

                            insertTab(str, null, cmp, null, a_targetIndex);
                            setTabComponentAt(a_targetIndex, tcmp);
                    } // if

                    setSelectedComponent(cmp);
                    return;
            } // if
            if (a_targetIndex < 0 || sourceIndex == a_targetIndex) {
                    return;
            } // if
            if (a_targetIndex == getTabCount()) {    
                    source.remove(sourceIndex);
                    addTab(str, cmp);
                    setTabComponentAt(getTabCount() - 1, tcmp);
                    setSelectedIndex(getTabCount() - 1);
            } else if (sourceIndex > a_targetIndex) {
                    source.remove(sourceIndex);
                    insertTab(str, null, cmp, null, a_targetIndex);
                    setTabComponentAt(a_targetIndex, tcmp);
                    setSelectedIndex(a_targetIndex);
            } else {
                    source.remove(sourceIndex);
                    insertTab(str, null, cmp, null, a_targetIndex - 1);
                    setTabComponentAt(a_targetIndex - 1, tcmp);
                    setSelectedIndex(a_targetIndex - 1);
            }

    }
于 2011-12-22T21:38:34.760 回答
2

将此添加到 isDragAcceptable 以避免异常:

boolean transferDataFlavorFound = false;
for (DataFlavor transferDataFlavor : t.getTransferDataFlavors()) {
    if (FLAVOR.equals(transferDataFlavor)) {
        transferDataFlavorFound = true;
        break;
    }
}
if (transferDataFlavorFound == false) {
    return false;
}
于 2013-12-06T21:11:17.630 回答