[Java swing动态添加自定义按钮

问题描述 投票:-1回答:1

我在以绝对布局将组件动态添加到JPanel时遇到问题(实际上不是添加,而是保存以前添加的组件)。我可以在框架的jpanel中插入一个自定义圆圈按钮,但是在显示之前添加的对象时遇到问题。

[差不多20年前,我编写了一个应用程序来测试最短路径和类似算法。看起来是这样的:我在面板上添加了标记节点,然后使用标记线将它们连接起来。然后,我计算了路径。我已经好几年没有使用Swing了。现在,我想在Swing中重复相同的任务。当然,我没有旧应用程序的副本。一开始我遇到了一个问题:将自定义组件动态添加到面板中。面板具有绝对布局。用户必须从菜单中选择“新节点”选项。然后出现一个对话框–应用程序要求输入新节点的名称。命名后,单击鼠标将节点添加到一个位置。但是我有一个问题,因为我无法显示之前添加的按钮-仅显示新的按钮。我试图将旧按钮及其坐标作为元组存储在列表中–我做错了。

这似乎很容易:Core Java书籍(MouseTest第10章第1卷)中显示了一个类似的应用程序,唯一的区别是作者绘制了一个包含鼠标单击创建的几何形状的组件,但我想绘制自己的自定义按钮(恰好有两种组件:自定义按钮和代表带有连接该按钮的距离标签的线的组件)。每个对象必须是一个单独的组件。请帮助我解决我的问题。

public class RoutingGame extends JFrame{
private List<ButtonTuple> buttonTuples;
private JMenuBar menuBar;
private JMenu fileMenu;
private JMenu opsMenu;

public RoutingGame() {
    super();
    buttonTuples = new ArrayList<>();
    setBounds(100, 100, 800, 600);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JPanel npanel = new JPanel();
    getContentPane().add(npanel, BorderLayout.NORTH);
    npanel.setLayout(new FlowLayout(FlowLayout.LEADING));
    JPanel cpanel = new JPanel();
    getContentPane().add(cpanel, BorderLayout.CENTER);
    cpanel.setLayout(null);

    menuBar = new JMenuBar();
    fileMenu = new JMenu("File");
    opsMenu = new JMenu("Options");
    JMenuItem newItem = new JMenuItem("New file");
    JMenuItem openItem = new JMenuItem("Open file");
    JMenuItem nodeItem = new JMenuItem("New node");
    JMenuItem closeItem = new JMenuItem("Close file");
    JMenuItem exitItem = new JMenuItem("Exit");
    JMenuItem dspItem = new JMenuItem("Display options");
    menuBar.add(fileMenu);
    menuBar.add(opsMenu);
    fileMenu.add(newItem);
    fileMenu.add(openItem);
    fileMenu.add(nodeItem);
    fileMenu.add(closeItem);
    fileMenu.add(exitItem);
    opsMenu.add(dspItem);
    setJMenuBar(menuBar);
    exitItem.addActionListener(e -> System.exit(0));
    nodeItem.addActionListener(e -> {
        String s = (String)JOptionPane.showInputDialog(
                this,
                "Choose a name for your new node:",
                "Add node",
                JOptionPane.PLAIN_MESSAGE,
                null,
                null,
                "Node");
        RoundButton roundButton = new RoundButton(s);
        Insets insets = cpanel.getInsets();
        Dimension size = roundButton.getPreferredSize();

        final int[] mouseCoord = {-10,-10};
        setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
        cpanel.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                mouseCoord[0] = e.getX();
                mouseCoord[1] = e.getY();
                for(var tuple : buttonTuples){// this loop doesn't work
                    var button = tuple.getRoundButton();
                    var xpos = tuple.getXPos();
                    var ypos = tuple.getYPos();
                    button.setBounds( xpos + insets.left  - size.width / 2, ypos + insets.top - size.height / 2, size.width  , size.height );
                }

                roundButton.setBounds ( insets.left + mouseCoord[0]  - size.width / 2, 
insets.top + mouseCoord[1] - size.height / 2, size.width  , size.height );
                buttonTuples.add( new ButtonTuple(roundButton, mouseCoord[0], mouseCoord[1]));
                setCursor(Cursor.getDefaultCursor());
                cpanel.add( roundButton );
                cpanel.revalidate();
                cpanel.repaint();
                }
            });
        });
    }    
}

 public class RoundButton extends JButton {
    private static final int DEFAULT_WIDTH = 66;
    private static final int DEFAULT_HEIGHT = 66;
    private Shape shape;
    public RoundButton(String label) {
        super(label);
        setContentAreaFilled(false);
}
@Override
public Dimension getPreferredSize() {
    return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}

protected void paintComponent(Graphics g) {
    int diameter = Math.min(getWidth(), getHeight());
    int radius = diameter / 2;

    if (! getModel().isArmed()) {
        g.setColor(WHITE);
    } else {
        g.setColor( new Color(230, 230, 230) );
    }
    g.fillOval(getWidth() / 2 - radius, getHeight() / 2 - radius, diameter, diameter);
    g.drawOval(getWidth() / 2 - radius, getHeight() / 2 - radius, diameter, diameter);
    super.paintComponent(g);
}

protected void paintBorder(Graphics g) {
    int diameter = Math.min(getWidth(), getHeight());
    int radius = diameter / 2;
    g.setColor(getForeground() );
    g.drawOval(getWidth() / 2 - radius, getHeight() / 2 - radius, diameter - 1, diameter - 1);
    }
}

public class ButtonTuple {
    private RoundButton roundButton;
    private int xPos;
    private int yPos;
    // + getters, setters and constructor
}

public class RoutingGameRunner {
    public static void main(String[] args) {
        EventQueue.invokeLater( () -> {
            try {
                RoutingGame window = new RoutingGame();
                window.setResizable(false);
                window.setVisible(true);
            } catch (Exception e) {
                 e.printStackTrace();
            }
        });
    }
}
java swing dynamic jbutton add
1个回答
0
投票

但是我有一个问题,因为我无法显示之前添加的按钮

为什么您必须再次显示按钮?如果将它们添加到面板中,除非您有删除它们的逻辑,否则它们不会消失。

无论如何,您是否进行了调试?

button.setBounds( xpos + mouseCoord[0]  - size.width / 2, ypos +  
    mouseCoord[1] - size.height / 2, size.width  , size.height );

上面的代码应重组为类似的内容:

int x = xpos + e.getX() - (size.width / 2);
int y = ypos + e.getY() - (size.height / 2);
button.setBounds(x, y, size.width, size.height);

因为现在您实际上可以添加一些调试代码以确保您的计算正确:

System.out.println(x + " : " + y + " : " + size);

上面的代码将验证:

  1. 您实际上调用了循环代码
  2. 按钮的边界是正确的

我猜位置不对,按钮被一个接一个地放置。

我不理解此逻辑:

roundButton.setBounds ( insets.left + mouseCoord[0]  - size.width / 2, insets.top + mouseCoord[1] - size.height / 2, size.width  , size.height );
buttonTuples.add( new ButtonTuple(roundButton, mouseCoord[0], mouseCoord[1]));

您一次添加了组件的位置,但是在ButtonTuple类中保存了另一个位置。

您为什么甚至需要最后两个参数?按钮的边界已经设置。

编辑:

除了上述所有建议之外,最大的问题是您需要创建RoundButton的新实例:

roundButton = new RoundButton();
roundButton.setBounds(…);

-2
投票

我在我的代码中发现了一个错误:内循环中的setBounds应该看起来像那样,但是不能再继续工作了:

button.setBounds( xpos + insets.left  - size.width / 2, ypos + insets.top - size.height / 2, size.width  , size.height );
© www.soinside.com 2019 - 2024. All rights reserved.