Search code examples
javaswingobserver-patternobservers

Observer pattern to print value from timer in JPanel


I've created a series of classes to try and figure out Observer patterns and am having some trouble. The two classes in the observer/observed relationship are ClockPanel, and TheTimer. TheTimer is a (swing) timer which keeps track of time from start in seconds. ClockPanel is a GUI (swing) which has a button to start the timer and a JLabel which I want to display the time.

The goal of my observer pattern: take the value being created in TheTimer and print it on my GUI.

The current problem: The timer is updating the time just fine, but I do not seem to understand how to update the value in my GUI.

I found a question similar to this one in a C# discussion, but the problem was more nuanced and way over my head.

Here are the five classes which comprise the program: 1. The GUI-ClockPanel

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class ClockPanel implements Observer {
JFrame frame;
JPanel panel;
JButton sbutton;
JLabel label;

@Override
public void update(int counter) {
    String clockval = String.valueOf(counter);
    label.setText(clockval);
}

public ClockPanel() {
    frame = new JFrame();
    frame.setSize(100, 100);
    panel = new JPanel();
    label = new JLabel();

    TheTimer myTimer = new TheTimer();

    sbutton = new JButton("start");
    sbutton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            myTimer.StartTimer();
        }
    });

    frame.setLayout(new FlowLayout());
    frame.add(panel);
    frame.add(sbutton);
    frame.add(label);
    frame.setVisible(true);

}
}

2. The Swing Timer-TheTimer

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.Timer;

public class TheTimer extends JPanel implements Subject {

private ActionListener action;
private Timer Time;
private int delay = 1000;
private ArrayList<Observer> observers = new ArrayList<Observer>();
private int counter = 0;

public TheTimer() {

    action = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.out.println(counter);
            counter++;
            setCounter(counter);
        }
    };
}

public void StartTimer() {
    Time = new Timer(delay, action);
    Time.setInitialDelay(0);
    Time.start();
}

public int getCounter() {
    return counter;
}

public void setCounter(int counter) {
    this.counter = counter;
    notifyObservers();
}

@Override
public void registerObserver(Observer observer) {
    observers.add(observer);
}

@Override
public void removeObserver(Observer observer) {
    observers.remove(observer);
}

@Override
public void notifyObservers() {
    for (Observer ob : observers) {
        System.out.println("Notifying ClockPanel on change in counter value");
        ob.update(this.counter);
    }
}
}

3. The Observer-Observer

public interface Observer {
public void update(int counter);
}

4. The Observer-related methods-Subject

public interface Subject {
public void registerObserver(Observer observer);

public void removeObserver(Observer observer);

public void notifyObservers();
}

5. The Main-TestMain

import javax.swing.SwingUtilities;

public class TestMain {

public static void main(String args[]) {

    ClockPanel panel = new ClockPanel();
    TheTimer timer = new TheTimer();
    timer.registerObserver(panel);

    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            new ClockPanel();
        }
    });
}
}

Solution

  • You have two TheTimer objects: one in ClockPanel, the other in TestMain#main().

    You need to remove the timer from (say) main() and add:

    myTimer.registerObserver(this);
    

    to your ClockPanel constructor.