为什么图形从 CardLayout 中消失了?

Why is the graphics disappearing from CardLayout?

我是编码的新手,这个社区非常有益。这是我第一次在阅读不同的相关问题后提出问题,我似乎仍然不明白。

因此,当我点击 loginButton(在另一个名为 LoginPage 的 class 中设置)时,它会显示“welcomePage”卡片,welcomePage(卡片)有两个按钮 backButton 和 drawButton。 Jpanel 是主要容器,添加到 Jframe(也在 class LoginPage 中设置)。我正在尝试绘画和设置背景(颜色);单击 drawButton 时在 welcomePage 上,但由于某种原因,在背景更改时绘画会立即显示并消失。另外当我把 paintComponent(welcomepanel.getGraphics());在没有“break;”的 while 循环中的方法,油漆确实保留但 window 卡住了(没有关闭)并且背景没有改变(为什么?)。

public class WelcomePage  implements ActionListener{

 JPanel welcomepanel = new JPanel();
JButton backButton = new JButton("Back");
JButton drawButton = new JButton("DRAW");

//Other codes elided

WelcomePage (String userID) {
 drawButton.addActionListener(this);

backButton.setBounds(50, 100, 100, 25);

    backButton.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            LoginPage.cardLayout.show(LoginPage.container, "loginpage");



        }
    });

    drawButton.setBounds(0, 0, 100, 25);

    drawButton.addActionListener(this);

   welcomepanel.setSize(420, 420);

    welcomepanel.add(backButton); //backs to the previous page (card)
    welcomepanel.add(drawButton);


 } //end constructor

public void paintComponent(Graphics g) {


     g = welcomepanel.getGraphics();

            g.drawString("CHASE", 200, 90);

            g.setColor(Color.yellow);

            for (int row = 0; row < 8; row++) {
                for (int col = 0; col < 8; col++) {

                    if (row % 2 == col % 2) {
                        g.setColor(Color.GRAY);
                    } else
                        g.setColor(Color.RED);
                    g.fillRect(100 + col * 40, 100 + row * 40, 40, 40);
                }

            }


}

 public void actionPerformed(ActionEvent e) {


    if (e.getActionCommand().equals("DRAW")) {

        welcomepanel.setBackground(Color.black);
        paintComponent(welcomepanel.getGraphics());
     // while (true) {paintComponent(welcomepanel.getGraphics());}


        welcomepanel.repaint();

        welcomepanel.revalidate();

    }
}

} //end class WelcomePage

我做错了什么?

我试过将欢迎页面扩展到 Jpanel 并直接在上面绘图,这很有效,但我想要一个特定的 Jpanel (welcomepanel) 实例来获取绘图。

This is what the paint looks like when welcome page extends Jpanel or when I use: while (true) {paintComponent(welcomepanel.getGraphics());}

我的第二个问题是,我想最终创建一个 GUI,“教师”可以用来添加测验类型的问题,学生可以用来参加这些测验,我会根据他们的登录凭据确定谁是谁,或者组合框选择什么的。使用 CardLayout 并以 Jpanel 作为主要容器来浏览不同的活动是个好主意还是我应该使用其他东西?

更新:这次更新我在actionperformed方法中使用了匿名class但是当drawButton被点击时仍然没有绘制:

WelcomePage.java:

 import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;

public class WelcomePage implements ActionListener {

    JPanel welcomepanel = new JPanel();
    JLabel welcomeLabel = new JLabel("QUIZ");
    JButton backButton = new JButton("Back");

    
    JButton drawButton = new JButton("DRAW");
    JButton whiteButton = new JButton("white");


    WelcomePage(String userID) {

        ///super();
        //  welcomepanel.setLayout(new GroupLayout(welcomepanel));
        welcomeLabel.setBounds(100, 0, 200, 35);
        welcomeLabel.setFont(new Font(null, Font.BOLD, 25));
        welcomeLabel.setText("Welcome " + userID);

        backButton.setBounds(50, 100, 100, 25);
        // backButton.setHorizontalAlignment(JButton.SOUTH);

        backButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                loginPage.cardLayout.show(loginPage.container, "loginpage");


            }
        });


        drawButton.setBounds(0, 0, 100, 25);

        drawButton.addActionListener(this);

        whiteButton.setBounds(200, 200, 100, 25);

        whiteButton.addActionListener(this);

        welcomepanel.setSize(420, 420);


        welcomepanel.add(welcomeLabel);
        welcomepanel.add(backButton);
        welcomepanel.add(drawButton);
        welcomepanel.add(whiteButton);


    }



    /**
     * Invoked when an action occurs.
     *
     * @param e the event to be processed
     */
    @Override
    public void actionPerformed(ActionEvent e) {

        if (e.getActionCommand().equals("DRAW")) {

            welcomepanel = new JPanel() {

                @Override
                public void paintComponent(Graphics g) {

                    super.paintComponent(g);

                    g.drawString("CHASE", 200, 90);

                    g.setColor(Color.yellow);

                    for (int row = 0; row < 8; row++) {
                        for (int col = 0; col < 8; col++) {

                            if (row % 2 == col % 2) {
                                g.setColor(Color.GRAY);
                            } else
                                g.setColor(Color.RED);
                            g.fillRect(100 + col * 40, 100 + row * 40, 40, 40);

                        }

                    }

                }

            };


            welcomepanel.setBackground(Color.black);

            welcomepanel.repaint();

            welcomepanel.revalidate();


            } else if (e.getActionCommand().equals("white")) {
                welcomepanel.setBackground(Color.blue);
            }
        }
    }

我在下面的 actionperformed 方法中创建了一个新的 welcomepage 实例 class 并调用了 welcomepages 的 welcomePanel 来获得一个让用户满意的“欢迎页面”。我想要的只是当我点击 drawButton 时,paintcomponent 方法按照 welcomepages 的 actionperformed 方法中的定义执行。我想探索使用在单个 class 中声明的不同 Jpanel 的可能性。也欢迎对我的代码的任何其他部分发表评论。

loginPage.java:

  import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;

public class loginPage implements ActionListener {

   static JFrame frame = new JFrame();
   static JPanel container = new JPanel();

    JPanel panel1 = new JPanel();
    JPanel panel2 = new JPanel();
    JPanel panel3 = new JPanel();


    JButton loginButton = new JButton("Login");
    JButton resetButton = new JButton("Reset");

    JButton logoutButton = new JButton("Logout");
    JButton hoemButton = new JButton("HOME");

    static CardLayout cardLayout = new CardLayout();


    JTextField userIDField = new JTextField();
    JPasswordField userPasswordField = new JPasswordField();

    JLabel IdLabel = new JLabel("userID:");
    JLabel userPasswordLabel = new JLabel("password:");
    JLabel messageLabel = new JLabel();

    HashMap<String, String> loginInfo = new HashMap<>(); //to hold the copy of the constructors' parameter value so it can be globally available for methods such as getters

    loginPage(HashMap<String, String> copyOfLoginInfo) {

        this.loginInfo = copyOfLoginInfo;  //@parra value copied into a global variable


       // this.container.setLayout(new BorderLayout());
       container.setLayout(cardLayout);

        IdLabel.setBounds(50, 100, 75, 25);
        IdLabel.setLocation(50, 100);
        userIDField.setBounds(125, 100, 200, 25);

        userPasswordLabel.setBounds(50, 150, 75, 25);
        userPasswordField.setBounds(125, 150, 200, 25);

        loginButton.setBounds(125, 200, 100, 25);
        loginButton.setFocusable(false);
        loginButton.addActionListener(this);

        hoemButton.setBounds(0, 0, 100, 25);
        logoutButton.setBounds(310, 0, 100, 25);
        logoutButton.setFocusable(false);

        resetButton.setBounds(220, 200, 100, 25);
        resetButton.setFocusable(false);
        resetButton.addActionListener(this);


        messageLabel.setBounds(125, 250, 250, 35);
        messageLabel.setFont(new Font(null, Font.ITALIC, 25));

        setPanel1();


        panel1.add(loginButton);
        panel1.add(resetButton);
        panel1.add(hoemButton);
        panel1.add(IdLabel);
        panel1.add(userIDField);
        panel1.add(userPasswordLabel);
        panel1.add(userPasswordField);
        panel1.add(messageLabel);

        panel2.add(logoutButton);
        setPanel2();

        //add panals to the JPanel container with their identifier string
        container.add(panel1, "loginPageage");
        container.add(panel2, "logoutpage");





        //this show method of cardLayout determines which panel..
        // ...should be shown at the start of the application, identifier string is used here
        cardLayout.show(container, "loginPageage");


        //when login is clicked go to logout page (panal 2)

        hoemButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                cardLayout.show(container, "logoutpage");
            }
        });

        //when logout is clicked go back to loginPageage

        logoutButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                cardLayout.show(container, "loginPageage");
            }
        });




        //add container to Jframe:


        frame.add(container);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(520, 520);
        frame.setLocationRelativeTo(null); //sets the window in the middle of pc screen
        //frame.setLayout(null); //this will make the cardlayout not show!!
        frame.setVisible(true);
        frame.setResizable(false);


    }




    /**
     * Invoked when an action occurs.
     *
     * @param e the event to be processed
     */
    @Override
    public void actionPerformed(ActionEvent e) {

        if (e.getSource() == resetButton) {

            userIDField.setText("");
            userPasswordField.setText("");
        }

        if (e.getSource() == loginButton) {

            String userID = userIDField.getText(); //gets user id text
            String password = String.valueOf(userPasswordField.getPassword()); //gets the password from the password field and then converts it into a string

            if (loginInfo.containsKey(userID)) {
                if (loginInfo.get(userID).equals(password)) {
                    messageLabel.setForeground(Color.cyan);
                    messageLabel.setText(String.format("%30s", "Login SUCCESSFUL"));



                    WelcomePage w = new WelcomePage(userID);
                    //add instance of welcome page to the Jpanel container, and
                    container.add(w.welcomepanel, "WelcomeP");
                    cardLayout.show(container, "WelcomeP");


                }

                        else {
                    messageLabel.setForeground(Color.red);
                    messageLabel.setText(String.format("%34s", "Invalid password!"));
                }
            } else {


                messageLabel.setForeground(Color.red);
                messageLabel.setText(String.format("%33s", "username not found!"));

            }
        }

    }

    void setPanel1() {

        panel1.setLayout(new BorderLayout()); //equivalent to setManaged(false)??
        panel1.setBounds(100, 100, 420, 420);
        panel1.setBackground(new Color(150, 150, 150));
        panel1.setBorder(BorderFactory.createBevelBorder(1));


    }

    void setPanel2(){

        panel2.setLayout(new BorderLayout()); //equivalent to setManaged(false)??
        panel2.setSize(420, 420);
        panel2.setBackground(Color.green);}

}


  

UserInfo.java

import java.util.HashMap;

public class UserInfo {

    HashMap<String, String> credentials = new HashMap<>();

    UserInfo () {

        credentials.put("user", "user");
    }

    protected HashMap<String, String> getCredentials() {
        return credentials;
    }
}

Main.java:

public class Main {

public static void main(String[] args) {

    UserInfo users = new UserInfo();

    loginPage loginP = new loginPage(users.getCredentials());
}

}

下面是您可能希望重构代码的示例(我省略了示例中的非必要代码)以使其正常工作。最值得注意的是,您会看到我使用 panelJPaneloverride paintComponent,调用 super.paintComponent然后在那里做所有的绘图工作。此外,我重写 getPreferredSize 以正确调整 JPanel 的大小,而不是 setSize.

更新:

根据您的评论:

首先我希望面板只显示按钮,然后当我按下 drawButton 时我希望绘图出现。:

只需创建一个最初设置为 false 的布尔值 canDraw,在绘制之前 paintComponent 检查 canDraw 布尔值和 return 如果它是 false。然后在 draw 按钮中 ActionListener 执行 canDraw = true;,然后调用 welcomPanel.repaint(); 这将导致调用 paintComponent 因此因为canDrawtrue 它应该绘制图形而不是 returning.

其他一些要记住的要点是:

  1. 不要使用 null/AbsoluteLayout 而是使用适当的 LayoutManager
  2. 不要在组件上调用 setBounds()setSize(),如果您使用正确的布局管理器,系统会为您处理此问题
  3. 所有 Swing 组件都应该通过 SwingUtilities.invokeLaterEDT 上调用(也许你这样做,但我把它放在这里是为了安全)
  4. 在将框架设置为可见之前调用 JFrame#pack()

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class WelcomePage {

    boolean canDraw = false;
    private JPanel welcomePanel;

    public WelcomePage() {
        createAndShowGui();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(WelcomePage::new);
    }

    private void createAndShowGui() {
        JFrame frame = new JFrame("WelcomePage");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // lets use a panel to hold our buttons which has a FlowLayoout and thus we dont need setSize or setBounds
        JPanel buttonsPanel = new JPanel();
        JButton backButton = new JButton("Back");
        JButton drawButton = new JButton("DRAW");
        drawButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                canDraw = true;
                // repaint will call jpanels paintComponent
                welcomePanel.repaint();
            }
        });
        buttonsPanel.add(backButton);
        buttonsPanel.add(drawButton);

        welcomePanel = new JPanel() {
            // override paintComponent of jpanel
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);

                // only draw when this is true
                if (!canDraw) {
                    return;
                }

                g.drawString("CHASE", 200, 90);
                g.setColor(Color.yellow);
                for (int row = 0; row < 8; row++) {
                    for (int col = 0; col < 8; col++) {
                        if (row % 2 == col % 2) {
                            g.setColor(Color.GRAY);
                        } else {
                            g.setColor(Color.RED);
                        }
                        g.fillRect(100 + col * 40, 100 + row * 40, 40, 40);
                    }

                }
            }

            // override getPreferredSize of jpanel to provide the correct size based on the drawing in paintComponent
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(600, 600);
            }
        };

        frame.add(buttonsPanel, BorderLayout.NORTH);
        frame.add(welcomePanel, BorderLayout.CENTER);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}