1
votes

I am brand new to coding and this community has been very beneficial. This is my first time asking after reading different related questions I still don't seem to get it.

So, when I hit loginButton (setup in another class called LoginPage) it shows the "welcomePage" card, the welcomePage (card) has two buttons backButton and drawButton. Jpanel is the main container, added to a Jframe (also set up in class LoginPage). I am trying to paint and setbackground(color); on the welcomePage when a drawButton is clicked, but for some reason, the paint shows and disappears immediately while the background changes. Also when I put the paintComponent(welcomepanel.getGraphics()); method in a while loop without "break;", the paint does stay but the window gets stuck (doesn't close) and the background doesn't change (why is that?).

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

What am I doing wrong?

I've tried extending the welcompage to Jpanel and directly drawing on it and that works but I instead want a particular instance of Jpanel (welcomepanel) to get the drawing.

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

My second question is, I want to ultimately create a GUI that "teachers" would use to add Quiz type questions and Students would use to take those quizzes, I'd determine who is who based on their login credentials or combo box selection or something. Is it a good idea to navigate through the different activities using CardLayout and with Jpanel as the main container or should I use something else?

Update:Update this time I used anonymous class in actionperformed method but when the drawButton is clicked still doesn't draw:

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

I created a new instance of the welcomepage in the actionperformed method in the following class and called welcomepages' welcomePanel to get a "welcome page" that greats the user. All I wanted is when I hit the drawButton the paintcomponent method to get executed as defined in welcomepages' actionperformed method. I wanted to explore the possibility of using different Jpanels declared in a single class. Feel free to comment on any other part of my code as well.

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());
}

}

1
What am I doing wrong? You don't call paintComponent directly. You call repaint and let Swing process the paint chain. You never call repaint or paintComponent in a loop. This blocks the GUI thread and freezes the GUI. As to your second question, it depends. For the student, a CardLayout with JPanels would be fine because you control the display of the JPanels. In other words, the student signs on, takes the quiz, gets his score, and logs off. - Gilbert Le Blanc
A teacher, on the other hand, may want to create a quiz, "take" the quiz (to see if his questions and answers are correct), and maybe look at a JTable of all of the students' scores for a quiz. A JTabbedPane might be more appropriate. You might want to take a look at this Stack Overflow answer. It creates a similar GUI to the one you want, although in the answer, the text is Spanish. - Gilbert Le Blanc
Also when you override paintcomponent always call super.paintComponent(g); as the first statement in the overriden method. Also keep your questions to 1 per post, as asking multiple because less useful to others - David Kroukamp
Also looking at the code paintComponent is not even overriding a valid method as your class is not a JPanel Id suggest posting a minimal reproducible example for better help sooner. - David Kroukamp
Don't use getGraphics() to do custom painting. See: Custom Painting for working examples on how to do custom painting. - camickr

1 Answers

1
votes

Below is an example of what you might want to refactor your code to look like (I have omitted non-essential code that you had in your example) in order to get it working. Most notably you will see I use a JPanel and override paintComponent of the panel , call super.paintComponent and then do all the drawing work inside there. Also I override getPreferredSize in order to size the JPanel correctly instead of setSize.

Update:

As per your comment:

first I wanted the panel to appear with just the buttons and then when I hit the drawButton then I want the drawing to appear.:

Simply create a boolean canDraw that is initially set to false, in paintComponent before drawing check the canDraw booleans value and return if it is false. Then in the draw buttons ActionListener do canDraw = true;, and then call welcomPanel.repaint(); which will cause paintComponent to be called and thus because the canDraw is true it should draw the graphics as opposed to returning.

Some other points to keep in mind are:

  1. Don't use a null/AbsoluteLayout rather use an appropriate LayoutManager
  2. Don't call setBounds() or setSize() on components, if you use a correct layout manager this will be handled for you
  3. All Swing components should be called on the EDT via SwingUtilities.invokeLater (perhaps you do this, but I put it here to be safe)
  4. Call JFrame#pack() before setting the frame to visible

enter image description here

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