如何在 Java 中使用 JButton 重绘组件?

Posted

技术标签:

【中文标题】如何在 Java 中使用 JButton 重绘组件?【英文标题】:How do I repaint a component by using a JButton in Java? 【发布时间】:2012-10-18 01:17:01 【问题描述】:

我正在尝试用 Java 编写一个名为 HighLow 的简单 GUI 骰子游戏。基本上,用户以 50 英镑的初始余额开始,他们可以选择三种不同的投注金额,如果他们选择低,骰子必须落在 2、3、4、5 或 6 上并支付 1:1,如果他们选择高,骰子必须落在 8、9、10、11 或 12 上,赔率为 1:1。如果他们选择 7,骰子必须落在 7 上,赔率为 4:1。有两个骰子,所以两者的值都应该相加。这不是我的问题的重点,只是想我会澄清这一点,以便您了解游戏的重点。

我对面板、事件处理和类似的东西完全陌生,所以我目前正在学习,但我在尝试解决这个问题时遇到了一些困难。我有五个类: Dice.java 包含一个 getFaceValue() 和 throwDie() 方法来模拟骰子的随机投掷。 DiceFace.java 创建带有点/点的骰子。 DiceFaceViewer.java 是框架类。 DiceFaceComponent 是两个骰子的组件,GamePanel 是所有 JButton 和 JComboBox 等所在的面板。

这就是我到目前为止所做的:创建一个框架,将带有按钮等的面板粘贴进去,然后将骰子图像添加到框架中。我想要的是每当我单击“投掷骰子”按钮时,骰子就会被重新绘制到带有随机面的框架上。如果我打开和关闭窗口,它现在所做的就是生成一张随机的脸。

骰子

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;
    

骰子脸

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);
    

骰子面组件

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();
    

游戏面板

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());
    

【问题讨论】:

【参考方案1】:

不要在任何paint 方法中调用repaint。这将导致重绘管理器安排另一个重绘事件,最终消耗您的 CPU 并停止您的编程。

改为调用 repaint 来获取更新潜水面值的动作侦听器。

骰子和骰子DiceFaceComponent之间也没有任何联系,它永远不会知道它应该绘制什么值。

另外,在您的DiceFace 课程中,我不会费心在每次抽奖时重新创建“点”,这只是一种浪费。而是在构造函数中创建它们。

paintComponent 方法也是如此,不必费心重新创建 DiceFace,只需在构造类时创建两个引用并根据需要更新它们的面值。

我会使用Dice 作为将所有不同元素联系在一起的基本模型。通过使用ChangeListerner 之类的东西,Dice 可以通知各种组件发生了变化,允许它们更新屏幕

【讨论】:

以上是关于如何在 Java 中使用 JButton 重绘组件?的主要内容,如果未能解决你的问题,请参考以下文章

Java 重绘未正确显示组件

Swing:如何在每个组件、JPanel、JButton 等上绘制动画?

如何从 JButton 中检索数据?

JPanel 图形清除和重绘?

Java - 从单独的组件中重绘组件

如何相对于另一个组件布局组件?