ОК, это довольно многословный ответ, но я просто хочу показать пример использования слушателей для более слабой связи и обновления JProgressBar
Скажем, у вас есть объект Player не из GUI, у которого есть свойство String name и свойство 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 не должен знать, в каком графическом интерфейсе он отображается, что будут делать наблюдатели с любой информацией, которую они наблюдают.
Теперь мы помещаем этого игрока в простой графический интерфейс, JPanel, у которого есть JSlider, который позволяет нам изменять состояние здоровья игрока. Фрагмент кода этого класса может выглядеть так:
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));
И мы бы обновили JProgressBar из метода setProgress:
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);
}
}