Search code examples
javaswinglabeljava-threads

Having problem with overlapping (Stacking) Label


Im trying to add a Score and Elapsed Time label (scoreAndTimer) to my already working snake game code. The problem is when I use scoreAndTimer.setText(); it stacks with previous text.

I tried to setText(); then setText(String); to clear previous one but it doesnt work also.


    private JLabel scoreAndTimer;
    private int sec, min;
    private Game game;


    public Frame() {

        JFrame frame = new JFrame();
        game = new Game();

        frame.add(game);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setTitle("Snake");
        frame.setResizable(false);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        scoreAndTimer = new JLabel();
        scoreAndTimer.setVerticalAlignment(SwingConstants.TOP);
        scoreAndTimer.setHorizontalAlignment(SwingConstants.CENTER);
        frame.add(scoreAndTimer);
        timer();
    }

    private void timer(){
        while(game.isRunning()){
            scoreAndTimer.setText("SCORE: "+(game.getSnakeSize()-3)+"                                       Elapsed Time: "+timeFormatter());
            try{
                if(sec == 60){
                    sec = 0;
                    min++;
                }
                sec++;
                Thread.sleep(1000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if(!game.isRunning())
            scoreAndTimer.setText("Game Over");
    }

    private String timeFormatter(){
        if(sec < 10 && min < 10)
            return "0"+min+":0"+sec;
        else if(sec >= 10 && min < 10)
            return "0"+min+":"+sec;
        else if(sec < 10 && min >= 10)
            return min+"0:"+sec;
        else
            return min+":"+sec;
    }

    public static void main(String[] args) {
        new Frame();
    }
}

Program is working well but could'nt prevent overlap. There is no error. Im using totally 3 Threads in my program, im not sure if threads are making a problem about this. Code is a bit long thats why i dont share the rest for now, if needed i can share other parts also but i dont think the problem occurs at other classes.


Solution

  • JFrame, or more precisely it contentpane uses BorderLayout by default.
    When you add components to a JFrame:

    frame.add(game);
    

    You implicitly add it to the BorderLayout.CENTER position, which is the default position. So frame.add(game); is equivalent to frame.add(game, BorderLayout.CENTER);
    The BorderLayout.CENTER position (as well as other BorderLayout positions) can hold one component. The problem is that you add another component to the same BorderLayout.CENTER position by:

    frame.add(scoreAndTimer);
    

    The solution is to add scoreAndTimer to a different position:

     frame.add(scoreAndTimer, BorderLayout.PAGE_END);
    

    and have

        frame.pack();
        frame.setVisible(true);
    

    at the end, after you have added all components.

    Important side note:
    timer() as written is not going to work. Think of Swing Application as an application that runs on a single thread. When this thread is busy with running the long while loop (like the one you have in timer(), it does not update the gui. The gui becomes unresponsive(freezes).
    Use Swing timer.