java - 尝试使用 JLayeredPane 拖动某些内容

标签 java swing drag-and-drop jlayeredpane

我正在尝试将拖放操作放入我的程序中;我发现以下示例说明了我正在尝试做的很多事情:

    package sandbox;
    
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Component;
    import java.awt.Container;
    import java.awt.Cursor;
    import java.awt.Dimension;
    import java.awt.GridLayout;
    import java.awt.LayoutManager;
    import java.awt.Point;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.awt.event.MouseMotionListener;
    
    import javax.swing.ImageIcon;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JLayeredPane;
    import javax.swing.JPanel;
    
    /**
     * Example showing the use of a JLayeredPane to implement dragging an object
     * across a JPanel containing other objects.
     * <P>
     * Basic idea: Create a JLayeredPane as a container, then put the JPanel containing
     * the application's components or whatever in the JLayeredPane.DEFAULT_LAYER layer of that layered pane.
     * The code is going to drag a JComponent object by calling JComponent.setPosition(x,y)
     * on the component. When a mouse is clicked on the panel to start the dragging, put the
     * component on the drag layer of the layered pane; as it is dragged, continue to call
     * setPosition to move it. When the mouse is released, use the x.y position of the release
     * to decide what to do with it next.   
     * 
     */
    public class ChessBoard extends JFrame implements MouseListener, MouseMotionListener
    {
      private static final long serialVersionUID = 1L;
        JLayeredPane layeredPane;
        JPanel chessBoard;
        JLabel chessPiece;
        int xAdjustment;
        int yAdjustment;
    
        public ChessBoard()
        {
            Dimension boardSize = new Dimension(600, 600);
    
            //  Use a Layered Pane for this application
    
            layeredPane = new JLayeredPane();
            layeredPane.setPreferredSize( boardSize );
            layeredPane.addMouseListener( this );
            layeredPane.addMouseMotionListener( this );
            getContentPane().add(layeredPane);
            
            //debug
            LayoutManager lm = layeredPane.getLayout();
            System.out.println("Layered pane layout name is " + (lm == null? "<null>" : lm.getClass().getName()));
    
            //  Add a chess board to the Layered Pane on the DEFAULT layer
            chessBoard = new JPanel();
            chessBoard.setLayout( new GridLayout(8, 8) );
            chessBoard.setPreferredSize( boardSize );
            chessBoard.setBounds(0, 0, boardSize.width, boardSize.height);
            layeredPane.add(chessBoard, JLayeredPane.DEFAULT_LAYER);
    
            //  Build the Chess Board squares
            // We use an 8x8 grid, and put a JPanel with BorderLayout on each square. 
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    JPanel square = new JPanel( new BorderLayout() );
                    square.setBackground( (i + j) % 2 == 0 ? Color.gray : Color.white );
                    chessBoard.add( square );
                }
            }
    
            // Add a few pieces to the board
            // we do this with an ImageIcon that gets added to the square's panel.
            ImageIcon duke = new ImageIcon("granary.gif");  // this is the image to add to each space.
            addDuke(duke, 0);
            addDuke(duke, 6);
            addDuke(duke, 15);
            addDuke(duke, 20);
        }
        
        private void addDuke(ImageIcon duke, int boardPosition)
        {
          JLabel pieceLabel = new JLabel(duke);
          JPanel piecePanel = (JPanel)chessBoard.getComponent(boardPosition);
          piecePanel.add(pieceLabel);
        }
    
        /*
        **  Add the selected chess piece to the dragging layer so it can be moved
        */
        public void mousePressed(MouseEvent e)
        {
          // get the component where the user pressed; iff that's not a panel,
          // we'll put it on the dragging layer.
            chessPiece = null;                                         // change1 swap the change1 lines
            // chessPiece = new JLabel(new ImageIcon("house1x1.gif")); // change1

            Component c =  chessBoard.findComponentAt(e.getX(), e.getY());
    
            if (c instanceof JPanel) return;
    
            // get the location of the panel containing the image panel, i.e.,
            // the square's panel. we adjust the location to which we move the
            // piece by this amount so the piece doesn't 'snap to' the cursor 
            // location.
            Point parentLocation = c.getParent().getLocation();
            xAdjustment = parentLocation.x - e.getX();
            yAdjustment = parentLocation.y - e.getY();
            chessPiece = (JLabel)c; // change2 - comment out
            chessPiece.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment);
    
            layeredPane.add(chessPiece, JLayeredPane.DRAG_LAYER); // evidently this removes it from the default layer also.
            layeredPane.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
        }
    
        /*
        **  Move the chess piece around
        */
        public void mouseDragged(MouseEvent me)
        {
            if (chessPiece == null) return;
    
            //  The drag location should be within the bounds of the chess board
    
            int x = me.getX() + xAdjustment;
            int xMax = layeredPane.getWidth() - chessPiece.getWidth();
            x = Math.min(x, xMax);
            x = Math.max(x, 0);
    
            int y = me.getY() + yAdjustment;
            int yMax = layeredPane.getHeight() - chessPiece.getHeight();
            y = Math.min(y, yMax);
            y = Math.max(y, 0);
    
            chessPiece.setLocation(x, y);   // evidently this works for whatever layer contains the piece.
            // also, the layout manager of its new home is evidently not the same as lower layers.
         }
    
        /*
        **  Drop the chess piece back onto the chess board
        */
        public void mouseReleased(MouseEvent e)
        {
            layeredPane.setCursor(null);
    
            if (chessPiece == null) return;
    
            //  Make sure the chess piece is no longer painted on the layered pane
    
            chessPiece.setVisible(false);
            layeredPane.remove(chessPiece);
            chessPiece.setVisible(true);
    
            //  The drop location should be within the bounds of the chess board
    
            int xMax = layeredPane.getWidth() - chessPiece.getWidth();
            int x = Math.min(e.getX(), xMax);
            x = Math.max(x, 0);
    
            int yMax = layeredPane.getHeight() - chessPiece.getHeight();
            int y = Math.min(e.getY(), yMax);
            y = Math.max(y, 0);
    
            Component c =  chessBoard.findComponentAt(x, y);
            Container parent = null;
            if (c instanceof JLabel)
            {
                parent = c.getParent(); // there's a piece on the square already; remove it from the panel.
                parent.remove(0);
            }
            else
            {
                parent = (Container)c;
            }
            parent.add( chessPiece );     // this adds the piece back to the default layer
            parent.validate();
        }
    
        public void mouseClicked(MouseEvent e) {}
        public void mouseMoved(MouseEvent e) {}
        public void mouseEntered(MouseEvent e) {}
        public void mouseExited(MouseEvent e) {}
    
        public static void main(String[] args)
        {
            JFrame frame = new ChessBoard();
            frame.setDefaultCloseOperation( DISPOSE_ON_CLOSE );
            frame.setResizable( false );
            frame.pack();
            frame.setLocationRelativeTo( null );
            frame.setVisible(true);
         }
    }

这适用于棋盘的情况,即允许用户将棋盘上的任何棋子拖动到不同的方格。

在我正在编写的应用程序中,被拖动的项目在用户单击开始拖动操作的某些内容之前并不存在。我无法弄清楚如何进行该创作并将其显示出来。

我当前的尝试是在标记为“change1”和“change2”的行上;将这两行与“change1”交换,并用“change2”注释掉这一行。换句话说,按下鼠标时创建 JLabel,并(希望)拖动它。但是当我运行它时,图像在按下或拖动期间不会显示,但会显示在拖动结束时的正方形上。

我在这里错过了什么?我对 JLayeredPane 有点困惑,javadoc 说它将遵循布局规则,但没有说明布局规则是否适用于所有层上的所有组件,或者仅适用于底部,或者适用于所有层但单独,什么?我不认为这是布局问题,但我不知道出了什么问题。我需要在某处进行某种 UI 更新吗?我认为添加该组件会使面板失效。

最佳答案

原始代码是假设您单击一个棋子而编写的。

现在您想要单击一个空单元格,这将需要进行以下更改。

  1. 棋盘由每个单元格中的 JPanel 组成。一些单元格将包含一个代表棋子的 JLabel。 mousePressed 事件中的当前逻辑期望您单击 JLabel,否则将跳过某些处理。

您需要删除:

//if (c instanceof JPanel) return;
  • 默认情况下,Swing 组件在创建时的大小为 0。
  • 您需要给它一个大小:

    chessPiece.setSize( chessPiece.getPreferredSize() );
    
  • 标签的定位逻辑基于查找被单击组件相对于父组件的位置。由于没有标签,该逻辑现在基于相对于分层 Pane 的面板。
  • 您需要调整此逻辑以使其再次相对于父面板:

    //Point parentLocation = c.getParent().getLocation();
    Point parentLocation = c.getLocation();
    

    我更新的 mousePressed 方法如下所示:

    public void mousePressed(MouseEvent e)
    {
      // get the component where the user pressed; iff that's not a panel,
      // we'll put it on the dragging layer.
        //chessPiece = null;                                         // change1 swap the change1 lines
        chessPiece = new JLabel(new ImageIcon("dukewavered.gif")); // change1
        chessPiece.setSize( chessPiece.getPreferredSize() );
    
        Component c =  chessBoard.findComponentAt(e.getX(), e.getY());
    
        //if (c instanceof JPanel) return;
    
        // get the location of the panel containing the image panel, i.e.,
        // the square's panel. we adjust the location to which we move the
        // piece by this amount so the piece doesn't 'snap to' the cursor
        // location.
        //Point parentLocation = c.getParent().getLocation();
        Point parentLocation = c.getLocation();
        xAdjustment = parentLocation.x - e.getX();
        yAdjustment = parentLocation.y - e.getY();
        //chessPiece = (JLabel)c; // change2 - comment out
        chessPiece.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment);
    
        layeredPane.add(chessPiece, JLayeredPane.DRAG_LAYER); // evidently this removes it from the default layer also.
        layeredPane.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
    }
    

    请注意,上述更改将破坏能够拖动现有标签的旧功能。如果您需要两者的功能,那么您的逻辑将取决于您单击 JLabel(在这种情况下您使用旧逻辑)还是单击 JPanel(在这种情况下您使用较新的逻辑)。

    关于java - 尝试使用 JLayeredPane 拖动某些内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64598967/

    相关文章:

    java - 是否可以使用 Hibernate Search 将 @IndexedEmbedded 字段存储在父/根中?

    java - 如何防止任务栏中的JFrame警报效果

    java - JFrame getHeight() 和 getWidth() 返回 0

    javascript - Jquery onDrop之后,如何Ajax GET并将PartialView的ActionResult附加到Div

    c# - Visual Studio 2010 C# 表单项目 - 拖放 - 最好的方法?

    java - 收集队列

    java - VLCJ 无法切换全屏

    java - 解析多部分响应

    java - 如何自动调整 JFrame 上图表的大小?

    C# WPF 拖放列表框 MVVM