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

问题描述

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

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

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,25);

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



        }
    });

    drawButton.setBounds(0,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

我做错了什么?

我已经尝试将 welcompage 扩展到 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,“教师”将使用该 GUI 添加测验类型的问题,而学生将用于参加这些测验,我将根据他们的登录凭据或组合框确定谁是谁选择什么的。使用 cardlayout 和 Jpanel 作为主容器浏览不同的活动是个好主意还是我应该使用其他东西?

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

欢迎页面.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,35);
        welcomeLabel.setFont(new Font(null,Font.BOLD,25));
        welcomeLabel.setText("Welcome " + userID);

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

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


            }
        });


        drawButton.setBounds(0,25);

        drawButton.addActionListener(this);

        whiteButton.setBounds(200,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",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,40);

                        }

                    }

                }

            };


            welcomepanel.setBackground(Color.black);

            welcomepanel.repaint();

            welcomepanel.revalidate();


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

我在下面的类中的 actionperformed 方法中创建了一个新的欢迎页面实例,并调用了welcomepages 的welcomePanel 来获得一个用户满意的“欢迎页面”。我想要的只是当我点击 drawButton 时,paintcomponent 方法按照welcomepages 的 actionperformed 方法中的定义执行。我想探索在单个类中声明使用不同 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,75,25);
        IdLabel.setLocation(50,100);
        userIDField.setBounds(125,25);

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

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

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

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


        messageLabel.setBounds(125,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,420,420);
        panel1.setBackground(new Color(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);}

}


  

用户信息.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 布尔值并返回如果它是 。然后在 draw 按钮中 ActionListenercanDraw = true;,然后调用 welcomPanel.repaint(); 这将导致 paintComponent 被调用,因此因为 {{1 }} 是 true 它应该绘制图形而不是返回。

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

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

enter image description here

JFrame#pack()