【问题标题】:Which Swing layout should i use for moving JButtons我应该使用哪种 Swing 布局来移动 JButton
【发布时间】:2017-11-28 22:18:00
【问题描述】:

我有一块 14x14 的板,它有 JButton,每个 Jbutton 都有不同的颜色。当您单击其中一个按钮时,它会检查具有相同颜色的邻居并将其删除。当它删除它们时,板之间有一个空白空间,所以上面的按钮应该向下移动以填充空白空间。我尝试使用 GridLayout,但我不知道如何移动上面的按钮。

【问题讨论】:

    标签: java swing


    【解决方案1】:

    这实际上是你几乎无法使用布局管理器的情况。

    LayoutManager 应该同时计算 所有 组件的布局。它由某些事件触发(例如,当父组件调整大小时)。然后它计算布局并相应地排列子组件。

    在你的情况下,情况完全不同。没有布局管理器可以明智地表示 上部按钮下降时出现的“中间”状态。 虽然组件是动画的,但它们不能成为正确布局的一部分。

    动画本身也可能有点棘手,但幸运的是可以通用解决。但是您仍然必须跟踪有关 where 每个组件(即每个按钮)当前位于网格中的信息。当一个按钮被移除时,您必须计算受其影响的按钮(即直接位于其上方的按钮)。这些必须是动画的。动画结束后,您必须为这些按钮分配新的网格坐标。

    以下是展示一种基本方法的 MCVE。它只是删除了被点击的按钮,但它应该很容易概括为删除其他按钮,基于其他条件。

    import java.awt.Point;
    import java.awt.Rectangle;
    import java.awt.event.ComponentAdapter;
    import java.awt.event.ComponentEvent;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class FallingButtons
    {
        public static void main(String[] args)
        {
            SwingUtilities.invokeLater(() -> createAndShowGui());
        }
    
        private static void createAndShowGui()
        {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            int rows = 8;
            int cols = 8;
            GridPanel gridPanel = new GridPanel(rows, cols);
            for (int r=0; r<rows; r++) 
            {
                for (int c=0; c<cols; c++) 
                {
                    JButton button = new JButton(r+","+c);
                    gridPanel.addComponentInGrid(r, c, button);
    
                    button.addActionListener(e -> 
                    {
                        Point coordinates = gridPanel.getCoordinatesInGrid(button);
                        if (coordinates != null)
                        {
                            gridPanel.removeComponentInGrid(
                                coordinates.x, coordinates.y);
                        }
                    });
                }
            }
            f.getContentPane().add(gridPanel);
    
            f.setSize(500, 500);
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        }
    }
    
    class GridPanel extends JPanel
    {
        private final int rows;
        private final int cols;
        private final JComponent components[][];
    
        GridPanel(int rows, int cols)
        {
            super(null);
            this.rows = rows;
            this.cols = cols;
            this.components = new JComponent[rows][cols];
    
            addComponentListener(new ComponentAdapter()
            {
                @Override
                public void componentResized(ComponentEvent e)
                {
                    layoutGrid();
                }
            });
        }
    
        private void layoutGrid()
        {
            int cellWidth = getWidth() / cols;
            int cellHeight = getHeight() / rows;
            for (int r=0; r<rows; r++) 
            {
                for (int c=0; c<cols; c++) 
                {
                    JComponent component = components[r][c];
                    if (component != null) 
                    {
                        component.setBounds(
                            c * cellWidth, r * cellHeight, cellWidth, cellHeight);
                    }
                }
            }
        }
    
        Point getCoordinatesInGrid(JComponent component)
        {
            for (int r=0; r<rows; r++) 
            {
                for (int c=0; c<cols; c++) 
                {
                    if (components[r][c] == component)
                    {
                        return new Point(r, c);
                    }
                }
            }
            return null;
        }
    
        void addComponentInGrid(int row, int col, JComponent component) 
        {
            add(component);
            components[row][col] = component;
            layoutGrid();
        }
    
        JComponent getComponentInGrid(int row, int col)
        {
            return components[row][col];
        }
    
        void removeComponentInGrid(int row, int col) 
        {
            remove(components[row][col]);
            components[row][col] = null;
    
            List<Runnable> animations = new ArrayList<Runnable>();
            for (int r=row-1; r>=0; r--)
            {
                JComponent component = components[r][col];
                if (component != null)
                {
                    Runnable animation = 
                        createAnimation(component, r, col, r + 1, col);
                    animations.add(animation);
                }
            }
            for (Runnable animation : animations)
            {
                Thread t = new Thread(animation);
                t.setDaemon(true);
                t.start();
            }
            repaint();
        }
    
        private Runnable createAnimation(JComponent component, 
            int sourceRow, int sourceCol, int targetRow, int targetCol)
        {
            int cellWidth = getWidth() / cols;
            int cellHeight = getHeight() / rows;
            Rectangle sourceBounds = new Rectangle(
                sourceCol * cellWidth, sourceRow * cellHeight, 
                cellWidth, cellHeight);
            Rectangle targetBounds = new Rectangle(
                targetCol * cellWidth, targetRow * cellHeight, 
                cellWidth, cellHeight);
            Runnable movement = createAnimation(
                component, sourceBounds, targetBounds);
            return () -> 
            {
                components[sourceRow][sourceCol] = null;
                movement.run();
                components[targetRow][targetCol] = component;
                repaint();
            };
        }
    
    
        private static Runnable createAnimation(JComponent component, 
            Rectangle sourceBounds, Rectangle targetBounds)
        {
            int delayMs = 10;
            int steps = 20;
            Runnable r = () ->
            {
                int x0 = sourceBounds.x;
                int y0 = sourceBounds.y;
                int w0 = sourceBounds.width;
                int h0 = sourceBounds.height;
    
                int x1 = targetBounds.x;
                int y1 = targetBounds.y;
                int w1 = targetBounds.width;
                int h1 = targetBounds.height;
    
                int dx = x1 - x0;
                int dy = y1 - y0;
                int dw = w1 - w0;
                int dh = h1 - h0;
    
                for (int i=0; i<steps; i++)
                {
                    double alpha = (double)i / (steps - 1);
    
                    int x = (int)(x0 + dx * alpha);
                    int y = (int)(y0 + dy * alpha);
                    int w = (int)(w0 + dw * alpha);
                    int h = (int)(h0 + dh * alpha);
                    SwingUtilities.invokeLater(() ->
                    {
                        component.setBounds(x, y, w, h);
                    });
                    try
                    {
                        Thread.sleep(delayMs);
                    }
                    catch (InterruptedException e)
                    {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
                SwingUtilities.invokeLater(() ->
                {
                    component.setBounds(x1, y1, w1, h1);
                });
            };
            return r;
        }
    
    
    
    }
    

    【讨论】:

      【解决方案2】:

      您可以尝试使用二维 JButton 数组

       JButton[][] buttons = new JButton[14][14];
      
       for (int i=0; i < buttons.length; i++) {
       for (int j=0; j < buttons[i].length; j++) {
          buttons[i][j] = new JButton("Button [" + i + "][" + j + "]");
       }
      }
      
      // Then do whatever,remove,change color,check next element in array
      // and compare colors etc
       buttons[2][3].setText("changed text");
      

      【讨论】:

        【解决方案3】:

        如果您希望以上按钮在移除组件时占用更多空间来填充空白空间,则使用GridLayout 是不可能的,但您可以添加一些空白组件,例如JLabels 来填充空间。

        为此,您可以使用Containeradd (Component comp, int index) 方法在容器中的特定索引处添加组件。

        此代码 sn-p 将用面板中的空白组件替换指定索引处的按钮(例如 45),该组件具有 GridLayout 集:

        JPanel boardPanel = new JPanel (new GridLayout (14, 14));
        
        // ... add your buttons ...
        
        // This code could be invoked inside an ActionListener ...
        
        boardPanel.remove (45);
        boardPanel.add (new JLabel (""), 45);
        boardPanel.revalidate ();
        boardPanel.repaint ();
        

        这样,其余组件不会移动,您只会看到一个空白区域替换您的按钮。

        您可以实现更多:如果在 index = 0 处添加空标签,所有按钮都会向右移动(请记住,组件的数量不应改变,否则组件将调整大小,您可能会获得不良行为) ,依此类推,您可以通过简单地移除单个组件并将其添加到不同的索引来“移动”它。

        另一种方法是存储代表模型逻辑的二维对象数组(您可以存储颜色和所有需要的东西),并通过覆盖 paintComponent 方法自行绘制它们。

        有关自定义绘画方法的示例,请查看this MadProgrammer's answer,其中他展示了如何突出显示网格中的特定单元格(在这种情况下,他使用 List 来存储对象,但二维数组也可以以及)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-03-30
          • 1970-01-01
          • 2015-06-05
          • 2013-03-13
          • 2012-01-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多