JPanel CardLayout causes visual glitch between swaps

43 views Asked by At

I am trying to create a JPanel with a CardLayout that switches between a menu and a forum when a button is pressed, but I am encountering a visual error where elements of the menu are still displaying when I switch to the forum.

(If relevant, my system specifications are: MacBook Air (13-inch, Early 2015), 2.2 GHz Dual-Core Intel Core i7, 8 GB 1600 MHz DDR3, Intel HD Graphics 6000 1536 MB, MacOS Monterey 12.6.7)

What follows is a simplified example of my basic implementation. Obviously certain features are not fully implemented in the example.

Main Class:

import javax.swing.*;

public class Main {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setContentPane(new MainPanel());
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }
}

MainPanel Class:

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

public class MainPanel extends JPanel {
    private final CardLayout layout = new CardLayout();
    private final Menu menuScreen;
    private final Forum forumScreen;
    public static final String MENU = "1";
    public static final String FORUM = "2";

    public MainPanel() {
        setLayout(layout);
        menuScreen = new Menu(this);
        add(menuScreen, MENU);
        forumScreen = new Forum(this);
        add(forumScreen, FORUM);
        layout.show(this, MENU);
    }

    public void navigate (String destination) {
        layout.show(this, destination);
        switch (destination) {
            case MENU -> menuScreen.updateUI();
            case FORUM -> forumScreen.updateUI();
        }
        this.revalidate();
        this.updateUI();
    }
}

Menu Class:

import java.awt.*;
import java.awt.event.ActionEvent;

public class Menu extends Screen {

    private final MainPanel panel;

    public Menu(MainPanel panel) {
        this.panel = panel;

        Button deleteButton = new Button("Delete");
        deleteButton.addActionListener(this::delete);
        add(deleteButton);

        Button loadButton = new Button("Load");
        loadButton.addActionListener(this::load);
        add(loadButton);

        Button forumButton = new Button("Forum");
        forumButton.addActionListener(this::toForum);
        add(forumButton);
    }

    private void toForum(ActionEvent actionEvent) {
        panel.navigate(MainPanel.FORUM);
    }

    private void load(ActionEvent actionEvent) {
    }

    private void delete(ActionEvent actionEvent) {
    }
}

Forum Class:

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

public class Forum extends Screen {

    private final MainPanel panel;
    private final JSpinner heightField = new JSpinner(new SpinnerNumberModel(1,1,Integer.MAX_VALUE,1));
    private final JSpinner widthField = new JSpinner(new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 1));

    public Forum(MainPanel panel) {
        this.panel = panel;

        add(new Label("Forum", Label.CENTER));

        add(new Label("Height"));
        add(heightField);

        add(new Label("Width"));
        add(widthField);

        Button exitButton = new Button("Exit");
        exitButton.addActionListener(this::exit);
        add(exitButton);
    }

    private void exit(ActionEvent actionEvent) {
        panel.navigate(MainPanel.MENU);
    }
}

Screen Class:

import javax.swing.*;

public class Screen extends JPanel {
    public Screen() {
        setBorder(BorderFactory.createEmptyBorder(30,30,30,30));
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    }
}

Functionally, the code succeeds at switching between the panels, but I've consistently been getting a visual glitch where elements of the last panel display and cover up elements on the new panel. I can still select the elements of the new panel that are covered up, and after I do so, they seem to be switched to the foreground in front of the glitched elements.

Menu Forum with Glitched Display

Note: this is part of a larger project so the UI seen here may not exactly match the code above. I will note, however, that a similar glitch has been seen throughout the project when using the CardLayout.

I have tried a variety of fixes. I rolled back my java version to java 11, but I still encountered the same problem. I made a loading card to force the ui to update with two switches, but I found that an empty loading card did not solve the problem and a loading card with elements still caused the visual glitch. As seen in the code, I tried a variety of calls that might update the panels such as revalidate, updateUI, and repaint, but nothing I have tried has worked.

Let me know if you have any suggestions.

1

There are 1 answers

1
FMrts On

It appears the visual glitch is resolved by switching from awt components (Button and Label) to swing lightweight components (JButton and JLabel). For instance, the menu class is:

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

public class Menu extends Screen {

    private final MainPanel panel;

    public Menu(MainPanel panel) {
        this.panel = panel;

        JButton deleteButton = new JButton("Delete");
        deleteButton.addActionListener(this::delete);
        add(deleteButton);

        JButton loadButton = new JButton("Load");
        loadButton.addActionListener(this::load);
        add(loadButton);

        JButton forumButton = new JButton("Forum");
        forumButton.addActionListener(this::toForum);
        add(forumButton);
    }

    private void toForum(ActionEvent actionEvent) {
        panel.navigate(MainPanel.FORUM);
    }

    private void load(ActionEvent actionEvent) {
    }

    private void delete(ActionEvent actionEvent) {
    }
}

Credit and thanks to hovercraft-full-of-eels