Chiranga Alwis
Chiranga Alwis

Reputation: 1111

How to remove and replace a JPanel using a button click?

I am relatively new to Java Concurrency and this is one of my first applications of such nature.

I have been trying to add a customized JPanel named Panel of Dice which displays a randomly generated 5 faces of dice when a button "Throw" is clicked. I am using two SwingWorker Threads HumanPlayer and SystemPlayer which updates their own throws and display the results on the JFrame using the above panel.

But the expected Panel replacements do not work as expected. The following is an incomplete Throw button ActionListener implementation. Can someone point out the issue with regards to my code?

this.throwButton = new JButton("Throw");
        this.throwButton.addActionListener(

                new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent event) {

                        int throwNumber = throwCount % 3;

                        if(throwNumber == 1 || throwNumber == 2) {
                            int response = JOptionPane.showConfirmDialog(null, "Do you want to reroll?");

                            if(response == 0) {
                                int number = Integer.parseInt(JOptionPane.showInputDialog("How many dice do you want throw?"));

                                if(number > 0 && number < 6) {
                                    container.remove(panel);
                                    container.remove(systemPanel);
                                    HumanPlayerCalculator human = new HumanPlayerCalculator(container, humanPlayer, game, currentScore, throwNumber, number, scoreButton);
                                    human.execute();
                                    throwCount++;
                                }
                                else {
                                    JOptionPane.showMessageDialog(null, "Invalid number entry.", "Input Error", JOptionPane.ERROR_MESSAGE);
                                }
                            }
                            else if(response == 1) {
                                if(throwNumber == 1) {
                                    humanPlayer.addScore(currentScore.getMandatoryThrow());
                                    throwCount += 2;
                                }
                                else if(throwNumber == 2) {
                                    humanPlayer.addScore(currentScore.getRerolls()[0]);
                                    throwCount += 1;
                                }
                                scoreButton.setEnabled(false);
                                newGameButton.setEnabled(true);
                            }
                            else {
                                //Do nothing
                            }
                        }
                        else {
                            container.remove(panel);
                            container.remove(systemPanel);
                            currentScore = new Score();
                            HumanPlayerCalculator human = new HumanPlayerCalculator(container, humanPlayer, game, currentScore, throwNumber, 5, scoreButton);
                            human.execute();
                            scoreButton.setEnabled(true);
                            systemScore = new Score();
                            SystemPlayerCalculator systemCalculator = new SystemPlayerCalculator(container, game.getSystemPlayer(), game, systemScore, throwNumber, 5);
                            systemCalculator.execute();
                            throwCount++;
                        }

                    }

                }

            );

PlayerCalculator.java

package ac.lk.iit.coursework2;

import java.awt.Container;
import java.io.Serializable;

import javax.swing.SwingWorker;

public abstract class PlayerCalculator extends SwingWorker<Throw, Object> implements Serializable {

    private Player player;
    private Game game;
    private Container container;
    private Score score;
    private int throwCount;
    private int diceCount;

    public PlayerCalculator() {

    }

    public PlayerCalculator(Player player, Game game, Container container, Score score, int count, int diceCount) {
        this.setPlayer(player);
        this.setGame(game);
        this.setContainer(container);
        this.setScore(score);
        this.setThrowCount(count);
        this.setDiceCount(diceCount);
    }

    public Player getPlayer() {
        return player;
    }

    public void setPlayer(Player player) {
        this.player = player;
    }

    public Game getGame() {
        return game;
    }

    public void setGame(Game game) {
        this.game = game;
    }

    public Container getContainer() {
        return container;
    }

    public void setContainer(Container container) {
        this.container = container;
    }

    public Score getScore() {
        return score;
    }

    public void setScore(Score score) {
        this.score = score;
    }

    public int getThrowCount() {
        return throwCount;
    }

    public void setThrowCount(int throwCount) {
        this.throwCount = throwCount;
    }

    public int getDiceCount() {
        return diceCount;
    }

    public void setDiceCount(int diceCount) {
        this.diceCount = diceCount;
    }

}

The HumanPlayerCalculator.java class

package ac.lk.iit.coursework2;

import java.awt.BorderLayout;
import java.awt.Container;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;

import javax.swing.JButton;
import javax.swing.JOptionPane;

public class HumanPlayerCalculator extends PlayerCalculator {

    private JButton button;

    public HumanPlayerCalculator() {
        super();
    }

    public HumanPlayerCalculator(Container container, HumanPlayer player, Game game, Score score, int throwCount, int count, JButton button) {
        super(player, game, container, score, throwCount, count);
        this.setDiceCount(count);
        this.setThrowButton(button);
    }

    public JButton getThrowButton() {
        return button;
    }

    public void setThrowButton(JButton throwButton) {
        this.button = throwButton;
    }

    @Override
    protected Throw doInBackground() throws Exception {
        Throw aThrow = new Throw(this.getDiceCount());
        ArrayList<Die> faces = aThrow.getFaces();
        //Testing
        System.out.println("You:");
        for(int i = 0 ; i < faces.size() ; i++) {
            System.out.print(faces.get(i).getValue() + " ");
        }
        System.out.println();
        System.out.println("Faces = " + faces.size());
        //Testing
        getContainer().add(new PanelOfDice("You", faces, "Current total = " + this.getPlayer().getScore()), BorderLayout.CENTER);
        this.getContainer().validate();
        this.getContainer().getComponent(0).repaint();
        return aThrow;
    }

    protected void done() {
        try {
            if(this.getThrowCount() == 0) {
                this.getScore().setMandatoryThrow(get());
            }
            else if(this.getThrowCount() == 1) {
                this.getScore().setRerolls(get(), 1);
            }
            else {
                this.getPlayer().addScore(get());
                this.getThrowButton().setEnabled(false);
            }
        }
        catch(ExecutionException executionException) {
            executionException.printStackTrace();
            JOptionPane.showMessageDialog(null, "Error in system. Please try again.", "System Error", JOptionPane.ERROR_MESSAGE);
        }
        catch(InterruptedException interruptedException) {
            interruptedException.printStackTrace();
            JOptionPane.showMessageDialog(null, "Error in system. Please try again.", "System Error", JOptionPane.ERROR_MESSAGE);
        }
    }

}

The SystemPlayerCalculator.java

package ac.lk.iit.coursework2;

import java.awt.BorderLayout;
import java.awt.Container;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;

import javax.swing.JOptionPane;

public class SystemPlayerCalculator extends PlayerCalculator {

    public SystemPlayerCalculator(Container container, SystemPlayer player, Game game, Score score, int throwCount, int count) {
        super(player, game, container, score, throwCount, count);
    }

    @Override
    protected Throw doInBackground() throws Exception {
        Throw aThrow = new Throw(this.getDiceCount());
        ArrayList<Die> faces = aThrow.getFaces();
        Thread.sleep(5000);
        //Testing
        System.out.println("Computer:");
        for(int i = 0 ; i < faces.size() ; i++) {
            System.out.print(faces.get(i).getValue() + " ");
        }
        System.out.println();
        System.out.println("Faces = " + faces.size());
        //Testing
        getContainer().add(new PanelOfDice("Computer", faces, "Current total = " + this.getPlayer().getScore()), BorderLayout.SOUTH);
        this.getContainer().validate();
        this.getContainer().getComponent(1).repaint();
        return aThrow;
    }

    protected void done() {
        try {
            if(this.getThrowCount() == 0) {
                this.getScore().setMandatoryThrow(get());
            }
            else if(this.getThrowCount() == 1) {
                this.getScore().setRerolls(get(), 1);
            }
            else {
                this.getPlayer().addScore(get());
            }
        }
        catch(ExecutionException executionException) {
            JOptionPane.showMessageDialog(null, "Error in system. Please try again.", "System Error", JOptionPane.ERROR_MESSAGE);
        }
        catch(InterruptedException interruptedException) {
            JOptionPane.showMessageDialog(null, "Error in system. Please try again.", "System Error", JOptionPane.ERROR_MESSAGE);
        }
    }

}

At this juncture the SystemPlayer's throw will replace the HumanPlayer's throw display after the thread's timed waiting although they are placed in different positions based on the BorderLayout. I can't figure out why this happens.

Can someone help me in this case?

Upvotes: 0

Views: 1260

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347184

One of the simplest choices is to use a CardLayout, see How to Use CardLayout for more details

enter image description here

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;

public class Test1 {

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

    public Test1() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JPanel content;
        private CardLayout cardLayout;

        private ColorPane colorPane;
        private NumberPane numberPane;

        public TestPane() {
            setLayout(new BorderLayout());

            colorPane = new ColorPane();
            numberPane = new NumberPane();

            cardLayout = new CardLayout();

            content = new JPanel(cardLayout);
            content.add(new JPanel(), "blank");
            content.add(colorPane, "colorPane");
            content.add(numberPane, "numberPane");

            add(content);

            JPanel panel = new JPanel();
            JButton rndColor = new JButton("Color");
            rndColor.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    ColorWorker colorWorker = new ColorWorker(colorPane);
                    colorWorker.execute();
                    cardLayout.show(content, "colorPane");
                }
            });
            JButton rndNumber = new JButton("Number");
            rndNumber.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    NumberWorker numberWorker = new NumberWorker(numberPane);
                    numberWorker.execute();
                    cardLayout.show(content, "numberPane");
                }
            });
            panel.add(rndColor);
            panel.add(rndNumber);

            add(panel, BorderLayout.SOUTH);
        }

        public class ColorPane extends JPanel {

            public ColorPane() {
            }

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(100, 100);
            }

        }

        public class ColorWorker extends SwingWorker<Color, Color> {

            private ColorPane colorPane;

            public ColorWorker(ColorPane colorPane) {
                this.colorPane = colorPane;
            }

            @Override
            protected void process(List<Color> chunks) {
                colorPane.setBackground(chunks.get(chunks.size() - 1));
            }

            @Override
            protected Color doInBackground() throws Exception {
                int randomColors = 1 + (int) (Math.random() * 100);
                Color color = null;
                for (int index = 0; index < randomColors; index++) {
                    int red = (int) (Math.random() * 255);
                    int green = (int) (Math.random() * 255);
                    int blue = (int) (Math.random() * 255);
                    color = new Color(red, green, blue);
                    publish(color);
                    Thread.sleep(40);
                }
                System.out.println(color);
                return color;
            }

            @Override
            protected void done() {
                try {
                    Color color = get();
                    colorPane.setBackground(color);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                } catch (ExecutionException ex) {
                    ex.printStackTrace();
                }
            }

        }

        public class NumberPane extends JPanel {

            private JLabel label;

            public NumberPane() {
                setLayout(new GridBagLayout());
                label = new JLabel("...");
                add(label);
            }

            public void setNumber(int number) {
                label.setText(Integer.toString(number));
                revalidate();
                repaint();
            }

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(100, 100);
            }

        }

        public class NumberWorker extends SwingWorker<Integer, Integer> {

            private NumberPane numberPane;

            public NumberWorker(NumberPane colorPane) {
                this.numberPane = colorPane;
            }

            @Override
            protected void process(List<Integer> chunks) {
                numberPane.setNumber(chunks.get(chunks.size() - 1));
            }

            @Override
            protected Integer doInBackground() throws Exception {
                int randomNumbers = (int) (Math.random() * 100);
                int number = 0;
                for (int index = 0; index < randomNumbers; index++) {
                    number = (int) (Math.random() * 1024);
                    publish(number);
                    Thread.sleep(40);
                }
                return number;
            }

            @Override
            protected void done() {
                try {
                    int number = get();
                    numberPane.setNumber(number);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                } catch (ExecutionException ex) {
                    ex.printStackTrace();
                }
            }

        }

    }

}

If you can't use a CardLayout, then you will need to call revalidate AND repaint on the parent container, as you want to force the parent container to update itself.

Upvotes: 1

Related Questions