MeDdErS
MeDdErS

Reputation: 11

How do I repaint a component by using a JButton in Java?

I'm trying to code a simple GUI dice game in Java called HighLow. Basically, the user starts with an initial balance of £50 and they have a choice of three different betting amounts and if they choose Low the dice must land on either 2, 3, 4, 5 or 6 and pays 1:1 and if they choose High the dice must land on either 8, 9, 10, 11 or 12 and pays 1:1. If they choose Sevens the dice must land on seven and pays 4:1. There are two dice so the value of both should be added. This is not the point of my question but just thought I'd clear that up so you understand the point of the game.

I'm totally new to panels, event handling and everything like that so I'm currently learning but I'm having some difficulty trying to work this out. I have five classes: Dice.java consists of a getFaceValue() and throwDie() method to simulate the random throw of a die. DiceFace.java creates dice with pips/dots on them. DiceFaceViewer.java is the frame class. DiceFaceComponent is the component for two dice and GamePanel is the panel where all the JButtons and JComboBox etc are located.

This is what I have done so far: created a frame, stuck the panel with the buttons etc in, and added the dice images to the frame. What I want is for the dice to be repainted onto the frame with a random face whenever I click the 'Throw Dice' button. All it is doing right now is generating a random face if I open and close the window.

Dice

import java.util.Random;
public class Dice {

    // Instance variables
    private int faceValue;
    private int sides;
    private Random generator;

    public Dice(int s) {
        generator = new Random();
        sides = s;
        faceValue = generator.nextInt(sides) + 1;
    }

    // Simulates the throw of a die landing on a random face.
    public int throwDie() {
        return faceValue = (int)(Math.random() * sides) + 1;
    }

    // Returns the current face value of the die
    public int getFaceValue() {
        return faceValue;
    }

    // Set face value of die
    public void setValue(int v) {
        faceValue = v;
    }
}

DiceFace

import java.awt.*;
import java.awt.geom.*;

public class DiceFace {

    // Holds the seven possible dot positions on a standard die
    private Ellipse2D.Double[] dots = new Ellipse2D.Double[7];

    private Rectangle box;
    private int xLeft;
    private int yTop;
    private int side;
    private int diceValue;

    public DiceFace(int s, int x, int y, int v) {
        side = s;       // Dimension of dice face
        xLeft = x;      // xPos
        yTop = y;       // yPos
        diceValue = v;  // Pip value
    }

    public void draw(Graphics2D g2) {
        box = new Rectangle(xLeft, yTop, side, side);
        makeDots();

        // Black background
        g2.setColor(Color.BLACK);
        g2.fill(box);

        // White dots on black
        g2.setColor(Color.WHITE);

        // Draw dots
        if (diceValue == 1) 
            g2.fill(dots[0]);

        else if (diceValue == 2) {
            g2.fill(dots[1]); g2.fill(dots[2]);
        }

        else if (diceValue == 3) {
            g2.fill(dots[0]); g2.fill(dots[1]); g2.fill(dots[2]);
        }

        else if (diceValue == 4) {
            g2.fill(dots[1]); g2.fill(dots[2]);
            g2.fill(dots[3]); g2.fill(dots[4]);
        }

        else if (diceValue == 5) {
            g2.fill(dots[0]); g2.fill(dots[1]);
            g2.fill(dots[2]); g2.fill(dots[3]); g2.fill(dots[4]);
        }

        else if (diceValue == 6) {
            g2.fill(dots[1]); g2.fill(dots[2]); g2.fill(dots[3]);
            g2.fill(dots[4]); g2.fill(dots[5]); g2.fill(dots[6]);
        }
    }

    public void makeDots() {

        int w = side/6; // Dot width
        int h = side/6; // Dot height

        dots[0] =  new Ellipse2D.Double(xLeft + (2.5 * side)/6,
                yTop + (2.5 * side)/6, h, w);

        dots[1] = new Ellipse2D.Double(xLeft + (3.75 * side)/6,
                yTop + (3.75 * side)/6, h, w);

        dots[2] = new Ellipse2D.Double(xLeft + (1.25 * side)/6,
                yTop + (1.25 * side)/6, h, w);

        dots[3] = new Ellipse2D.Double(xLeft + (1.25 * side)/6,
                yTop + (3.75 * side)/6, h, w);

        dots[4] = new Ellipse2D.Double(xLeft + (3.75 * side)/6,
                yTop + (1.25 * side)/6, h, w);

        dots[5] =  new Ellipse2D.Double(xLeft + (1.25 * side)/6,
                yTop + (2.5 * side)/6, h, w);

        dots[6] =  new Ellipse2D.Double(xLeft + (3.75 * side)/6,
                yTop + (2.5 * side)/6, h, w);
    }
}

DiceFaceViewer

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

public class DiceFaceViewer {
    public static void main(String[] args) {

        // Create frame
        JFrame frame = new JFrame();

        // Set frame attributes
        frame.getContentPane().setPreferredSize(new Dimension(360, 320));
        frame.pack();
        frame.setTitle("High Low");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);

        // Set location of frame to centre screen
        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();

        int w = frame.getSize().width;
        int h = frame.getSize().height;
        int x = (dim.width-w)/2;
        int y = (dim.height-h)/2;
        frame.setLocation(x, y);

        DiceFaceComponent component = new DiceFaceComponent();
        frame.add(component);

        // Add panel to frame
        GamePanel panel = new GamePanel();
        frame.add(panel, BorderLayout.NORTH);

        frame.setVisible(true);
    }
}

DiceFaceComponent

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

public class DiceFaceComponent extends JComponent {

    // Instance variables
    private int faceValue1;
    private int faceValue2;

    public DiceFaceComponent() {
        faceValue1 = new Dice(6).getFaceValue();
        faceValue2 = new Dice(6).getFaceValue();
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        // Recover Graphics2D
        Graphics2D g2 = (Graphics2D) g;

        DiceFace dice1 = new DiceFace(75, 66, 160, faceValue1); // s, x, y, v
        dice1.draw(g2);
        repaint();

        DiceFace dice2 = new DiceFace(75, 219, 160, faceValue2);
        dice2.draw(g2);
        repaint();
    }
}

GamePanel

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

public class GamePanel extends JPanel {

    private int balance = 50;

    public GamePanel() {

        // Components
        JRadioButton highButton = new JRadioButton("High");
        JRadioButton lowButton = new JRadioButton("Low");
        JRadioButton sevensButton = new JRadioButton("Sevens");

        // Button group
        ButtonGroup bg = new ButtonGroup();
        bg.add(highButton);
        bg.add(lowButton);
        bg.add(sevensButton);

        add(highButton);
        add(lowButton);
        add(sevensButton);

        // Array to hold bet amounts
        String betAmounts[] = {"£10", "£5", "£1"};

        // Bets combo box
        JComboBox bets = new JComboBox(betAmounts);
        add(bets);

        // Balance
        JLabel bal = new JLabel(" Balance: £" + balance);
        add(bal);

        // Throw dice button
        final JButton throwButton = new JButton("Throw Dice");
        add(throwButton, FlowLayout.RIGHT);

        class Action implements ActionListener {

            Dice dice = new Dice(6);

            public void actionPerformed(ActionEvent e) {

                dice.throwDie();
                dice.getFaceValue();
            }
        }

        throwButton.addActionListener(new Action());
    }
}

Upvotes: 0

Views: 1576

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347314

Don't call repaint from within any paint method. This will cause the repaint manager to schedule a another repaint event, eventually consuming your CPU and stalling your programming.

Instead call repaint for the actions listener that is updating the dive's face value.

There is also no connection between the dice and the dice DiceFaceComponent, it will never know what values it should be drawing.

Also, in your DiceFace class, I wouldn't bother recreating the "dots" on each draw, it just a waste. Create them in the constructor instead.

The same goes for the paintComponent method, don't bother recreating the DiceFace, simply create two references when you construct the class and update their face values as needed.

I would use the Dice as the base model that ties all the various elements together. By using something like a ChangeListerner, it would be possible for the Dice to notify the various components that a change has occurred allowing them to update the screen

Upvotes: 1

Related Questions