如何根据另一个类的更改状态更新进度条?

时间:2019-03-10 16:47:57

标签: java swing

我目前正在开发一个游戏,并且我试图实现一个健康栏,该健康栏会在每次玩家受到伤害时更新,我目前已经对其进行了设置,使其位于布局的单独面板中:

// menu is the bit with the health bar
ControlPanel menu = new ControlPanel(this);
window.add(menu.getMainPanel(), BorderLayout.NORTH);
// view is the part of the window the game runs in
window.add(view, BorderLayout.CENTER);

我听说要对其进行更新,您可以使用changelistener,但是我一直很难使它正常运行,这就是我到目前为止所要做的:

private JProgressBar healthProgressBar;
private Player player;

public ControlPanel(Game game) {
    player = game.getPlayer();

    healthProgressBar.setString(player.getHealth() + "/100");
    healthProgressBar.setStringPainted(true);
    healthProgressBar.setValue(player.getHealth());
    healthProgressBar.addChangeListener(new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            int oldHealth = player.getHealth();
            player = (Player) e.getSource();
            if (player.getHealth() != oldHealth) {
                healthProgressBar.setValue(player.getHealth());
            }
        }
    });

public JPanel getMainPanel() {
    return mainPanel;
}

它将使进度条显示在我想要的位置,并具有我赋予的值,但是如果有人知道如何使它起作用,它将不会在玩家受到伤害时更新。非常感谢!

1 个答案:

答案 0 :(得分:2)

好的,这是一个有点冗长的答案,但我只想展示一个使用侦听器进行松耦合和更新JProgressBar的示例

假设您有一个非GUI Player对象,该对象具有String名称属性和int health属性,并使用属性更改支持来允许添加侦听器。此类的摘要如下所示:

class Player {
    public static final String HEALTH = "health"; // the name of the property of interest
    public static final int HEALTH_MIN = 0;
    public static final int HEALTH_MAX = 100;
    private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
    private String name;
    int health = 50; // value from 1 to 100

    public Player(String name) {
        this.name = name;
    }

    public int getHealth() {
        return health;
    }

    // any time this is called, listeners will be notified through the pcSupport object
    public void setHealth(int health) {
        int oldValue = this.health;
        int newValue = health;
        this.health = health;

        // notify observers of change in the HEALTH property
        pcSupport.firePropertyChange(HEALTH, oldValue, newValue);
    }

    public String getName() {
        return name;
    }

    // allow listeners/observers to be added
    public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(name, listener);
    }

    // ....... other methods

}

请注意解耦-Player类不需要知道它显示在哪个GUI中,观察者将如何使用他们观察到的任何信息。

现在,我们将这个Player放在一个简单的GUI中,它是一个具有JSlider的JPanel,允许我们更改玩家的health属性的状态。此类的代码片段可能类似于:

class PlayerPanel extends JPanel {
    private Player player;
    private JSlider healthSlider = new JSlider(Player.HEALTH_MIN, Player.HEALTH_MAX, 0); 

    public PlayerPanel(Player player) {
        this.player = player;

        healthSlider.setValue(player.getHealth());
        healthSlider.setMajorTickSpacing(20);
        healthSlider.setMinorTickSpacing(5);
        healthSlider.setPaintTicks(true);
        healthSlider.setPaintLabels(true);

        // call method to change the player's health when the slider changes
        healthSlider.addChangeListener(clEvent -> healthChange());

        JPanel namePanel = new JPanel();
        namePanel.add(new JLabel("Player Name:"));
        namePanel.add(Box.createHorizontalStrut(5));
        namePanel.add(new JLabel(player.getName()));

        setLayout(new BorderLayout());
        add(namePanel, BorderLayout.PAGE_START);
        add(healthSlider);
    }

    // ........ other methods


    private void healthChange() {
        player.setHealth(healthSlider.getValue());
    }

    // .......... other methods
}

然后在另一个具有JProgressBars的类中,我们可以在progressBar中显示播放器的健康状况,并在此健康状况发生变化时让侦听器更新progressBar:

// create a Player object
Player player = new Player(playerName);

// create a JProgressBar to show the health
final JProgressBar progressBar = new JProgressBar(Player.HEALTH_MIN, Player.HEALTH_MAX);
progressBar.setStringPainted(true);
progressBar.setValue(player.getHealth()); // set initial value

// add a PropertyChangeListener to Player to be notified when HEALTH changes
player.addPropertyChangeListener(Player.HEALTH, pcEvent -> setProgress(progressBar, player));            

我们将从setProgress方法更新JProgressBar:

private void setProgress(JProgressBar progressBar, Player player) {
    int health = player.getHealth();
    progressBar.setValue(health);
}

整个工作示例如下:

import java.awt.BorderLayout;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Window;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;

@SuppressWarnings("serial")
public class JProgressHealth extends JPanel {
    public static final String[] PLAYER_NAMES = {"John", "Steve", "Frank", "Judy", "Hillary", "Billy"};
    private List<Player> players = new ArrayList<>();

    public JProgressHealth(Window owner) {
        setLayout(new GridLayout(0, 1));
        for (String playerName : PLAYER_NAMES) {
            Player player = new Player(playerName);
            final JProgressBar progressBar = new JProgressBar(Player.HEALTH_MIN, Player.HEALTH_MAX);
            progressBar.setStringPainted(true);
            progressBar.setValue(player.getHealth());
            player.addPropertyChangeListener(Player.HEALTH, pcEvent -> setProgress(progressBar, player));            
            JPanel titlePanel = new JPanel(new BorderLayout());
            titlePanel.add(progressBar);
            titlePanel.setBorder(BorderFactory.createTitledBorder(player.getName()));
            add(titlePanel);

            // create player panels to illustrate how this works
            PlayerPanel playerPanel = new PlayerPanel(player);
            JDialog dialog = new JDialog(owner, player.getName(), ModalityType.MODELESS);
            dialog.add(playerPanel);
            dialog.pack();
            dialog.setLocationByPlatform(true);
            dialog.setVisible(true);
        }
    }

    // make JPanel wider
    @Override
    public Dimension getPreferredSize() {
        Dimension superSize = super.getPreferredSize();
        int w = 3 * superSize.width;
        int h = superSize.height;
        return new Dimension(w, h);
    }

    private void setProgress(JProgressBar progressBar, Player player) {
        int health = player.getHealth();
        progressBar.setValue(health);
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("Health");

        JProgressHealth mainPanel = new JProgressHealth(frame);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

@SuppressWarnings("serial")
class PlayerPanel extends JPanel {
    private Player player;
    private JSlider healthSlider = new JSlider(Player.HEALTH_MIN, Player.HEALTH_MAX, 0); 

    public PlayerPanel(Player player) {
        this.player = player;

        healthSlider.setValue(player.getHealth());
        healthSlider.setMajorTickSpacing(20);
        healthSlider.setMinorTickSpacing(5);
        healthSlider.setPaintTicks(true);
        healthSlider.setPaintLabels(true);
        healthSlider.addChangeListener(clEvent -> healthChange());

        JPanel namePanel = new JPanel();
        namePanel.add(new JLabel("Player Name:"));
        namePanel.add(Box.createHorizontalStrut(5));
        namePanel.add(new JLabel(player.getName()));

        setLayout(new BorderLayout());
        add(namePanel, BorderLayout.PAGE_START);
        add(healthSlider);
    }

    // make JPanel wider
    @Override
    public Dimension getPreferredSize() {
        Dimension superSize = super.getPreferredSize();
        int w = 2 * superSize.width;
        int h = superSize.height;
        return new Dimension(w, h);
    }


    private void healthChange() {
        player.setHealth(healthSlider.getValue());
    }

    public Player getPlayer() {
        return player;
    }
}

class Player {
    public static final int HEALTH_MIN = 0;
    public static final int HEALTH_MAX = 100;
    public static final String HEALTH = "health";
    private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
    private String name;
    int health = 50; // value from 1 to 100

    public Player(String name) {
        this.name = name;
    }

    public int getHealth() {
        return health;
    }

    public void setHealth(int health) {
        int oldValue = this.health;
        int newValue = health;
        this.health = health;

        // notify observers of change
        pcSupport.firePropertyChange(HEALTH, oldValue, newValue);
    }

    public String getName() {
        return name;
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.removePropertyChangeListener(listener);
    }

    public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(name, listener);
    }

    public void removePropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.removePropertyChangeListener(name, listener);
    }
}