Home > front end >  Java&Swing: why the componenet isn't added dynamically?
Java&Swing: why the componenet isn't added dynamically?

Time:12-01

The following is my code.

I think click the button, at least, a Color.CYAN block will be added into MainPanel, but it doesn't.

Could you please tell me how to achieve that? Thanks.

public class TestFrame extends JFrame {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            final TestFrame mainFrame = new TestFrame();
            mainFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
            mainFrame.setVisible(true);
        });
    }

    public TestFrame() throws HeadlessException {
        setTitle("Frame");
        setSize(new Dimension(1000, 800));
        final JPanel mainPanel = new JPanel();

        final JButton button = new JButton("Test");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println(e.getActionCommand());
                mainPanel.add(new Unit());
            }
        });

        mainPanel.add(button);
        mainPanel.revalidate();

        add(mainPanel);
    }

    class Unit extends JComponent {
        public Unit() {
            setSize(new Dimension(100, 100));
            setBackground(Color.CYAN);
        }

        @Override
        protected void paintComponent(Graphics g) {
            final Graphics2D g2D = (Graphics2D) g;
            g2D.drawString("Hello World", 10, 10);
        }
    }
}

CodePudding user response:

Your Unit JComponent is likely being added to mainPanel in the ActionListener and thus the GUI, but it has no preferred size and so per the FlowLayout used by JPanels, it will size to [0, 0]. FlowLayouts (and most layout managers) do not respect a component's size but rather its preferredSize. Also, revalidate() and repaint() need to be called on the container (mainPanel) after Unit has been added so that the layout managers can do their laying out of components and to allow the OS to clear dirty pixels.

To solve this, give it a preferred size, preferably by overriding public Dimension getPreferredSize() but by calling setPreferredSize(...) if you must, and by calling revalidate() and repaint() after adding the component to the container.

Better still, add the component to the container using a CardLayout tutorial, but hide it by also adding an empty JLabel, again using a CardLayout, and then display the hidden component by calling CardLayout.show(...) from within ActionListener.

Side note: don't forget the super method within your painting method:

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); // add this ****
        final Graphics2D g2D = (Graphics2D) g;
        g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2D.setFont(UNIT_FONT);
        g2D.drawString("Hello World", textX, textY);
    }

else you break the painting chain and may see unwanted artifacts or other problems


e.g.,

import java.awt.*;
import javax.swing.*;

@SuppressWarnings("serial")
public class TestFrame extends JFrame {
    private CardLayout cardLayout = new CardLayout();
    private JPanel cardPanel = new JPanel(cardLayout);
    
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            final TestFrame mainFrame = new TestFrame();
            mainFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
            mainFrame.pack();
            mainFrame.setLocationRelativeTo(null);
            mainFrame.setVisible(true);
        });
    }

    public TestFrame() throws HeadlessException {
        setTitle("Frame");
        setPreferredSize(new Dimension(1000, 800));
        
        
        final JPanel mainPanel = new JPanel(new BorderLayout());
        
        final JButton button = new JButton("Test");
        button.addActionListener(e -> {
            cardLayout.next(cardPanel);
        });
        JPanel btnPanel = new JPanel();
        btnPanel.add(button);

        mainPanel.add(btnPanel, BorderLayout.PAGE_END);
        mainPanel.add(cardPanel);
        add(mainPanel);
        
        cardPanel.add(new JLabel(), "Foo");
        cardPanel.add(new Unit(), Unit.class.getCanonicalName());
    }

    static class Unit extends JPanel {
        private static final int PREF_W = 100;
        private static final int PREF_H = 100;
        private static final Font UNIT_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 60);

        public Unit() {
            setBackground(Color.CYAN);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(PREF_W, PREF_H);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            final Graphics2D g2D = (Graphics2D) g;
            g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g2D.setFont(UNIT_FONT);
            g2D.drawString("Hello World", 360, 350);
        }
    }
}
  • Related