【问题标题】:How do you update a progress bar based on another class changing state?如何根据另一个类更改状态更新进度条?
【发布时间】:2019-03-10 16:47:56
【问题描述】:

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

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

我听说要使其更新,您可以使用更改侦听器,但我在让它正常运行时遇到了一些困难,这就是我目前所拥有的:

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] 模块之间关系的强度。 [2] en.wikipedia.org/wiki/Coupling_(computer_programming).
  • @FailingCoder:ChangeListener 是观察者设计模式的一个版本,因此请查看How does the Observer pattern reduce coupling? 以及Good patterns for loose coupling in Java?more links
  • @FailingCoder:你为什么要发布耦合定义的链接?我想我很了解这个概念,谢谢
  • 致原始发帖人——对于在 cmets 中偏离轨道感到抱歉。您需要创建并发布一个有效的minimal reproducible example 以获得最佳答案,而无需我们猜测,因为这将使我们能够完全理解您的代码设置。
  • 事实上,您发布的代码看起来可能有引发 NullPointerException 的风险,因为我们看不到您如何或是否初始化 healthProgressBar 变量。

标签: java swing


【解决方案1】:

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

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

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 中,观察者将如何处理他们观察到的任何信息。

现在我们把这个播放器放在一个简单的 GUI 中,一个带有 JSlider 的 JPanel,它允许我们改变播放器的健康属性的状态。此类代码的 sn-p 可能如下所示:

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

【讨论】:

    猜你喜欢
    • 2019-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-30
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    • 2020-05-29
    相关资源
    最近更新 更多