javaswinguser-interfacedrawstring

String won't display using drawString()


I'm writing a simple Trivia game and I ran into a problem where the question string won't display In my frame I'm using border layout with "new game" and "end game" on the bottom (South) and my Trivia panel in the center. The Trivia panel is made of grid layout with 2 rows and 1 column with the 4 answers on the bottom half and on the upper half I used another panel with border layout with the question string in the center and the score in the east. It should look like this: enter image description here

However all of the components display except for the question string. My paintComponent is:

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.setFont(font);
    g.drawString(tf,0,0);
}

where tf is my string and my Trivia panel code is:

public TriviaPanel(){
    score = 0;
    scoreString = new JTextField();
    scoreString.setFont(font);
    questionPanel = new JPanel();
    questionPanel.setLayout(new BorderLayout());
    questionPanel.add(scoreString,BorderLayout.EAST);
    this.setLayout(new GridLayout(2,1));
    pool = null;
    try {
        pool = new Pool();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    question = pool.getQuestion();
    String[] answers = question.shuffle();
    tf = question.getQuestion();
    this.add(questionPanel);
    answersPanel = new JPanel();
    answersPanel.setLayout(new GridLayout(2,2));
    buttons = new JButton[NUM_OF_ANSWERS];
    for (int i = 0; i < NUM_OF_ANSWERS; i++) {
        buttons[i] = new JButton(answers[i]);
        answersPanel.add(buttons[i]);
        buttons[i].addActionListener(lis);

    }
    this.add(answersPanel);
scoreString.setText("Score: "+score+"/"+pool.getIterator()*CORRECT);
}

where pool is used to hold my pool of questions. When I debugged the code I see the tf string being updated to the question string but it won't display. Is it because of the coordinates? Any insights would be greatly appreciated.

[Edit] Although not finished but full code below:

import java.util.Arrays;
import java.util.Collections;

public class Question {
    private final int NUM_OF_ANSWERS = 4;
    private String question;
    private String[] answers = new String[NUM_OF_ANSWERS];
    private final int CORRECT_ANSWER = 0;

    public Question(String qu, String an, String dm1, String dm2, String 
dm3){
        question = qu;
        answers[0] = an;
        answers[1] = dm1;
        answers[2] = dm2;
        answers[3] = dm3;
    }

    public String getCorrectAnswer() {
        return answers[CORRECT_ANSWER];
    }

    public String getQuestion(){
        return question;
    }

    public String[] getAnswers(){
        return answers;
    }
    public String toString(){
        String str = question;
        for (int i = 0; i<4; i++)
            str+=" "+answers[i];
        str+="\n";
        return str;
    }
    public String[] shuffle(){
        String[] shuffled = new String[NUM_OF_ANSWERS];
        for (int i=0;i<NUM_OF_ANSWERS;i++)
            shuffled[i]=answers[i];
        Collections.shuffle(Arrays.asList(shuffled));
        return shuffled;
    }
}


import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Scanner;

public class Pool {

    private ArrayList<Question> questions = new ArrayList<>();
    private Scanner input = new Scanner(new File("src/trivia.txt"));
    private static int iterator = 0;

    public Pool() throws FileNotFoundException {
        while (input.hasNext()){
            String q = input.nextLine();
            String a = input.nextLine();
            String d1 = input.nextLine();
            String d2 = input.nextLine();
            String d3 = input.nextLine();
            Question question = new Question(q,a,d1,d2,d3);
            questions.add(question);
        }
        Collections.shuffle(questions);
        //System.out.println(questions);
    }

    public Question getQuestion(){
        Question q = questions.get(iterator);
        iterator++;
        return q;
    }

    public int getSize(){
        return questions.size();
    }

    public static int getIterator() {
        return iterator;
    }
}

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

public class GameFrame extends JFrame {
private JButton restart, finish;
private JPanel buttons;
    public GameFrame(){
        super("Trivia");
        TriviaPanel tp = new TriviaPanel();
        this.setLayout(new BorderLayout());
        this.add(tp,BorderLayout.CENTER);
        restart = new JButton("New game");
        finish = new JButton("End game");
        buttons = new JPanel();
        buttons.add(restart);
        buttons.add(finish);
        this.add(buttons,BorderLayout.SOUTH);
        this.setSize(1000,600);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public static void main(String[] args) {
        GameFrame gf = new GameFrame();
    }

}

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;
import java.util.concurrent.TimeUnit;

public class TriviaPanel extends JPanel {
    private TimerListener tl = new TimerListener();
    private Timer timer = new Timer(10000,tl);
    private static int score;
    private JTextField scoreString;
    private final int CORRECT = 10, INCORRECT = 5;
    private JButton[] buttons;
    private Pool pool;
    private Question question;
    private JButton pressed;
    private final int NUM_OF_ANSWERS = 4;
    private Listener lis = new Listener();
    //private JPanel questionPanel;
    private JPanel answersPanel;
    private String tf;
    private Font font = new Font("Serif",Font.BOLD,24);
    private JTextField tf2 = new JTextField();
    private QuestionPanel questionPanel;
public TriviaPanel(){
        score = 0;
        scoreString = new JTextField();
        scoreString.setFont(font);
        questionPanel = new QuestionPanel();
        //questionPanel.setLayout(new BorderLayout());
        //questionPanel.add(scoreString,BorderLayout.EAST);
        this.setLayout(new GridLayout(2,1));
        pool = null;
        try {
            pool = new Pool();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        question = pool.getQuestion();
        String[] answers = question.shuffle();
        tf = question.getQuestion();
        //tf2.setText(question.getQuestion());
        //questionPanel.add(tf2,BorderLayout.CENTER);
        this.add(questionPanel);
        answersPanel = new JPanel();
        answersPanel.setLayout(new GridLayout(2,2));
        buttons = new JButton[NUM_OF_ANSWERS];
        for (int i = 0; i < NUM_OF_ANSWERS; i++) {
            buttons[i] = new JButton(answers[i]);
            answersPanel.add(buttons[i]);
            buttons[i].addActionListener(lis);

        }
        this.add(answersPanel);
    timer.start();
    scoreString.setText("Score: "+score+"/"+pool.getIterator()*CORRECT);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        questionPanel.repaint();
    }

    private void next(){
        Question q = pool.getQuestion();
        question=q;
        tf = q.getQuestion();
        String[] answers = q.shuffle();
        for (int i = 0; i < NUM_OF_ANSWERS; i++)
            buttons[i].setText(answers[i]);

    }

    private void gameOver(){
        JOptionPane.showConfirmDialog(null,
                "Score: "+score, "Select an Option...", JOptionPane.YES_NO_CANCEL_OPTION);
    }

    private void check(String guess) {
        timer.stop();
        String answer = question.getCorrectAnswer();
        if (guess.equals(answer)) {
            score += CORRECT;
            tf = "Correct!!!";
        } else {
            score -= INCORRECT;
            tf = "Wrong answer";
        }
        scoreString.setText("Score: "+score+"/"+pool.getIterator()*CORRECT);
        repaint();
        try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }

    private class QuestionPanel extends JPanel{

    public QuestionPanel(){
            this.setLayout(new BorderLayout());
            this.add(scoreString,BorderLayout.EAST);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setFont(font);
            g.drawString(tf,0,200);
        }
    }

    private class Listener implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent e) {
            pressed = (JButton) e.getSource();
            String guess = pressed.getText();
            check(guess);
            if (pool.getIterator() < pool.getSize()) {
                timer.restart();
                next();
            }
            else
                gameOver();
        }

    }

    private class TimerListener implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent e) {
            timer.stop();
            score-=INCORRECT;
            scoreString.setText("Score: "+score+"/"+pool.getIterator()*CORRECT);
            repaint();
            timer.restart();
            next();
        }
    }
}

Solution

  • Start by getting rid of

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        questionPanel.repaint();
    }
    

    Seriously this is just dangerous and could set you for a endless loop which will consume your CPU cycles

    Next, I modified your QuestionPanel so the text is actually rendered somewhere within the realms of probability ...

    private class QuestionPanel extends JPanel {
    
        public QuestionPanel() {
            this.setLayout(new BorderLayout());
            this.add(scoreString, BorderLayout.EAST);
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setFont(font);
            FontMetrics fm = g.getFontMetrics();
            int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
            g.drawString(tf, 10, y);
        }
    }
    

    But seriously, why aren't you just using a JLabel?

    Now, any time tf changes, you need to call QuestionPanel's repaint method.

    So, that's in the next and check(String) methods.

    And finally (for now), you should never, ever do...

    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    

    inside the context of the event dispatching thread. This is stopping the repaints from been processed, so, for one whole second, nothing will change.

    If you want to stop the user for a second, disable the buttons and/or other controls and use another Swing Timer (for simplicity)

    This is demonstrated in this self contained, compilable and runnable example...

    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    import java.awt.Font;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.GridLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.FileNotFoundException;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.Timer;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    GameFrame frame = new GameFrame();
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class GameFrame extends JFrame {
    
            private JButton restart, finish;
            private JPanel buttons;
    
            public GameFrame() {
                super("Trivia");
                TriviaPanel tp = new TriviaPanel();
                this.setLayout(new BorderLayout());
                this.add(tp, BorderLayout.CENTER);
                restart = new JButton("New game");
                finish = new JButton("End game");
                buttons = new JPanel();
                buttons.add(restart);
                buttons.add(finish);
                this.add(buttons, BorderLayout.SOUTH);
                this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
    
        }
    
        public static class TriviaPanel extends JPanel {
    
    //        private TimerListener tl = new TimerListener();
    //        private Timer timer = new Timer(10000, tl);
            private static int score;
            private JTextField scoreString;
            private final int CORRECT = 10, INCORRECT = 5;
            private JButton[] buttons;
            private Pool pool;
            private Question question;
            private JButton pressed;
            private final int NUM_OF_ANSWERS = 4;
            private Listener lis = new Listener();
            //private JPanel questionPanel;
            private JPanel answersPanel;
            private String tf;
            private Font font = new Font("Serif", Font.BOLD, 24);
            private JTextField tf2 = new JTextField();
            private QuestionPanel questionPanel;
    
            public TriviaPanel() {
                score = 0;
                scoreString = new JTextField();
                scoreString.setFont(font);
                questionPanel = new QuestionPanel();
                //questionPanel.setLayout(new BorderLayout());
                //questionPanel.add(scoreString,BorderLayout.EAST);
                this.setLayout(new GridLayout(2, 1));
                pool = null;
                try {
                    pool = new Pool();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                question = pool.getQuestion();
                String[] answers = question.shuffle();
                tf = question.getQuestion();
                //tf2.setText(question.getQuestion());
                //questionPanel.add(tf2,BorderLayout.CENTER);
                this.add(questionPanel);
                answersPanel = new JPanel();
                answersPanel.setLayout(new GridLayout(2, 2));
                buttons = new JButton[NUM_OF_ANSWERS];
                for (int i = 0; i < NUM_OF_ANSWERS; i++) {
                    buttons[i] = new JButton(answers[i]);
                    answersPanel.add(buttons[i]);
                    buttons[i].addActionListener(lis);
    
                }
                this.add(answersPanel);
    //            timer.start();
                scoreString.setText("Score: " + score + "/" + pool.getIterator() * CORRECT);
            }
    
    //    @Override
    //    protected void paintComponent(Graphics g) {
    //        super.paintComponent(g);
    //        questionPanel.repaint();
    //    }
            private void next() {
                Question q = pool.getQuestion();
                question = q;
                tf = q.getQuestion();
                String[] answers = q.shuffle();
                for (int i = 0; i < NUM_OF_ANSWERS; i++) {
                    buttons[i].setText(answers[i]);
                }
                questionPanel.repaint();
            }
    
            private void gameOver() {
                JOptionPane.showConfirmDialog(null,
                        "Score: " + score, "Select an Option...", JOptionPane.YES_NO_CANCEL_OPTION);
            }
    
            private void check(String guess) {
    //            timer.stop();
                String answer = question.getCorrectAnswer();
                if (guess.equals(answer)) {
                    score += CORRECT;
                    tf = "Correct!!!";
                } else {
                    score -= INCORRECT;
                    tf = "Wrong answer";
                }
                questionPanel.repaint();
                scoreString.setText("Score: " + score + "/" + pool.getIterator() * CORRECT);
                // OH GOD THIS IS A BAD IDEA!
    //            try {
    //                TimeUnit.SECONDS.sleep(1);
    //            } catch (InterruptedException e) {
    //                e.printStackTrace();
    //            }
    
                Timer timer = new Timer(1000, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        Timer timer = (Timer) e.getSource();
                        timer.stop();
                        // Personally, I'd use a listener, but this will do
                        afterCheckDelay();
                    }
                });
                timer.start();
            }
    
            protected void afterCheckDelay() {
                if (pool.getIterator() < pool.getSize()) {
                    //timer.restart();
                    next();
                } else {
                    gameOver();
                }
            }
    
            private class QuestionPanel extends JPanel {
    
                public QuestionPanel() {
                    this.setLayout(new BorderLayout());
                    this.add(scoreString, BorderLayout.EAST);
                }
    
                @Override
                protected void paintComponent(Graphics g) {
                    super.paintComponent(g);
                    g.setFont(font);
                    FontMetrics fm = g.getFontMetrics();
                    int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
                    g.drawString(tf, 10, y);
                }
            }
    
            private class Listener implements ActionListener {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    pressed = (JButton) e.getSource();
                    String guess = pressed.getText();
                    check(guess);
                }
    
            }
    
            private class TimerListener implements ActionListener {
    
                @Override
                public void actionPerformed(ActionEvent e) {
    //                timer.stop();
    //                score -= INCORRECT;
    //                scoreString.setText("Score: " + score + "/" + pool.getIterator() * CORRECT);
    //                repaint();
    //                timer.restart();
    //                next();
                }
            }
        }
    
        public static class Pool {
    
            private ArrayList<Question> questions = new ArrayList<>();
    //        private Scanner input = new Scanner(new File("src/trivia.txt"));
            private static int iterator = 0;
    
            public Pool() throws FileNotFoundException {
                Question question = new Question("Why am I doing this", "Because I'm awesome", "You feel for the trick", "You have no idea", "To much caffine");
                questions.add(question);
    //            while (input.hasNext()) {
    //                String q = input.nextLine();
    //                String a = input.nextLine();
    //                String d1 = input.nextLine();
    //                String d2 = input.nextLine();
    //                String d3 = input.nextLine();
    //                Question question = new Question(q, a, d1, d2, d3);
    //                questions.add(question);
    //            }
                Collections.shuffle(questions);
                //System.out.println(questions);
            }
    
            public Question getQuestion() {
                Question q = questions.get(iterator);
                iterator++;
                return q;
            }
    
            public int getSize() {
                return questions.size();
            }
    
            public static int getIterator() {
                return iterator;
            }
        }
    
        public static class Question {
    
            private final int NUM_OF_ANSWERS = 4;
            private String question;
            private String[] answers = new String[NUM_OF_ANSWERS];
            private final int CORRECT_ANSWER = 0;
    
            public Question(String qu, String an, String dm1, String dm2, String dm3) {
                question = qu;
                answers[0] = an;
                answers[1] = dm1;
                answers[2] = dm2;
                answers[3] = dm3;
            }
    
            public String getCorrectAnswer() {
                return answers[CORRECT_ANSWER];
            }
    
            public String getQuestion() {
                return question;
            }
    
            public String[] getAnswers() {
                return answers;
            }
    
            public String toString() {
                String str = question;
                for (int i = 0; i < 4; i++) {
                    str += " " + answers[i];
                }
                str += "\n";
                return str;
            }
    
            public String[] shuffle() {
                String[] shuffled = new String[NUM_OF_ANSWERS];
                for (int i = 0; i < NUM_OF_ANSWERS; i++) {
                    shuffled[i] = answers[i];
                }
                Collections.shuffle(Arrays.asList(shuffled));
                return shuffled;
            }
        }
    }