简体   繁体   中英

java swing dynamically adding custom button

I have a problem with adding components dynamically to JPanel with absolute layout (in fact not with adding but with saving previously added components). I can insert one custom circle button into a jpanel on a frame but I have a problem with displaying objects I added before.

Almost 20 years ago I wrote an application to test shortest path and similar algorithms. It looked like that: I added labeled nodes to a panel and then connected them using a labeled line. Then I calculated paths. I haven't used Swing from several years. Now I'd like to repeat the same task in Swing. Of course I have no copy of my old app. I've got a problem on the beginning: dynamically adding custom components to a panel. The panel has an absolute layout. User has to choose “New node” option from menu. Then a dialog box appears – application asks for a name of the new node. After putting the name the node is added in a place when the mouse is clicked. But I have a problem because I can't display buttons added earlier – only new ones are displayed. I tried to store old buttons together with their coordinates as a tuple in a list – I did sth wrong.

It seemed to be easy: a similar application is shown in Core Java book (MouseTest 10th chapter 1st volume), the only difference is author draws one component containing geometrical shapes created on mouse-click but I 'd like to draw my custom buttons (precisely two kinds of components: custom buttons and a component representing a line with a distance label connecting the buttons ). Each object has to be a separate component. Please help me to solve my problem.

public class RoutingGame extends JFrame {
private Set<String> labelNames;
private JMenuBar menuBar;
private JMenu fileMenu;

public RoutingGame() {
    super();
    labelNames = new HashSet<>();
    setBounds(300, 200, 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");
    JMenuItem nodeItem = new JMenuItem("New node");
    menuBar.add(fileMenu);
    fileMenu.add(nodeItem);
    setJMenuBar(menuBar);
    nodeItem.addActionListener(e -> {
        String s = "";
        while (s.isBlank()) {
            s = (String) JOptionPane.showInputDialog(
                    this,
                    "Choose a name for your new node:",
                    "Add node",
                    JOptionPane.PLAIN_MESSAGE,
                    null,
                    null,
                    "Node");
            }
        final String[] nodeName = {s};
        setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
        cpanel.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                RoundButton roundButton = new RoundButton(nodeName[0]);
                Insets insets = cpanel.getInsets();
                Dimension size = roundButton.getPreferredSize();

                roundButton.setBounds ( insets.left + e.getX()  - size.width / 2, insets.top + e.getY() - size.height / 2, size.width  , size.height );                    
                setCursor(Cursor.getDefaultCursor());
                cpanel.add( roundButton );
                cpanel.revalidate();
                cpanel.repaint();
                cpanel.removeMouseListener(this);
                }
            });
        });
    }
}
 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 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();
            }
        });
    }
}

But I have a problem because I can't display buttons added earlier

Why do you have to display the buttons again? If you added them to the panel they won't disappear unless you have logic to remove them.

In any case, have you done any debugging?

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

Code like the above should be restructured to something like:

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

Because now you can actually add some debug code to make sure your calculations are correct:

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

Code like the above will verify that:

  1. you actually invoke your looping code
  2. the bounds of the button is correct

I'm guessing the location is wrong and the buttons are being placed on top of one another.

I'm not understanding this logic:

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]));

You add the component at once location, but then save a different location in the ButtonTuple class.

Why do you even need the last two parameters? The bounds of the button has already been set.

Edit:

In addition to all the above suggestions, the biggest problem is that you need to create a new instance of the RoundButton:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM