好的,这是一个有点冗长的答案,但我只是想展示一个使用侦听器来实现更松散耦合和更新 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);
}
}