Showing posts with label swing. Show all posts
Showing posts with label swing. Show all posts

Wednesday, 16 December 2009

Playing with the Mouse

In this article I'll be discussing how we can you can enhance an graphical component by handling mouse events such as mouse clicks and movements.

This article will not discuss the graphical aspect and assumes that you know how to create a simple UI component extending the JComponent and adding this component into a JFrame. For more information about how to do this, please read the article: Practical Example of Simple Java UI Component.

A note of caution to the reader: This is not an exhaustive article about events and some topics are simple mentioned while others not discussed at all. While you're encouraged to learn as much as you can, it's best to first read this topic as provided here in the order provided and then expand your knowledge once you have read it.

Create an Environment

Let's start by create a simple components (that does nothing) which will add into a frame.

package com.albertattard.pwtm;

import java.awt.Graphics;
import javax.swing.JComponent;

@SuppressWarnings("serial")
public class MyComponent extends JComponent {
  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
  }
}

Always start with a super simple class and then slowly evolve it testing each change on the way. Don't wait for bulk changes to test as then it may be too late to understand what's wrong.

Now it's time to create the frame and add the above component to it.

package com.albertattard.pwtm;

import java.awt.Color;
import javax.swing.BorderFactory;
import javax.swing.JFrame;

@SuppressWarnings("serial")
public class MyFrame extends JFrame {

  private MyComponent myComp;

  public MyFrame() {
    initComponents();
  }

  private void initComponents() {
    setLayout(null);

    myComp = new MyComponent();
    myComp.setBorder(
      BorderFactory.createLineBorder(Color.RED));
    myComp.setBounds(10, 10, 400, 200);
    add(myComp);
  }
}

Now let's kick off this example by creating another with the main method and have this show rolling

package com.albertattard.pwtm;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class MyMain {
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        createShowFrame();
      }
    });
  }

  private static void createShowFrame() {
    JFrame frame = new MyFrame();
    frame.setTitle("Playing with the Mouse");
    frame.setSize(600, 400);
    frame.setDefaultCloseOperation(
      JFrame.EXIT_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}

Now that we have all the require components in place let's run. The above three classes should produce the following (or something very similar).


Let's start playing

Java provides three methods/mechanisms that allow us, the developers, to use to handle mouse events. Let say that we want to change the background colour of my component whenever the mouse hovers over it and rest the colour when the mouse moves away from the component.

We want that our component (the class called MyComponent defined above) to do something when the mouse is moved over it. Then, we have to register a mouse listener within this component and act accordingly. The JComponent class (which MyComponent inherits from) has all the logic we need, that is, methods that allow us to register mouse listeners. Whenever mouse events are occur, these methods will advise/notify our listeners with the changes and we can act accordingly. We first start by registering a mouse listener and then implement the methods we're interested in as illustrated below.

package com.albertattard.pwtm;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JComponent;

@SuppressWarnings("serial")
public class MyComponent extends JComponent {

  public MyComponent() {
    initListeners();
  }

  private void initListeners() {
    addMouseListener(new MouseAdapter() {
      public void mouseExited(MouseEvent e) {
        setBackground(Color.WHITE);
        repaint();
      }

      public void mouseEntered(MouseEvent e) {
        setBackground(Color.RED);
        repaint();
      }
    });
  }

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    g.setColor(getBackground());
    g.fillRect(0, 0, getWidth(), getHeight());
  }
}

Hold your horses, I'm lost.

There's lot of new code in this class, so let's start understanding this bit by bit. Let's start with the initListeners() method. This method is registering a mouse listener by calling the addMouseListener () method inherited from the JComponent class and pass our listener. For simplicity, some code was removed.

  private void initListeners() {
    addMouseListener(/* The Mouse Listener*/);
  }

After removing all the other code, this method becomes very simple and understandable. If you refer to the previous example, we also included a constructor which is calling this method, which I don't believe it requires explanation. The paintComponent () method was also enhanced which now fills a rectangle with the set colour. This rectangle has the same size of our component which makes it appear as the background colour.

Let's now turn our attention to the MouseListener. What is it? Whenever a mouse event is fired, (in laymen terms) the JVM advise our listener about the change. It does so by execute one of the five methods defined by the MouseListener listed below:

  • mouseClicked()
  • mouseEntered()
  • mouseExited()
  • mousePressed()
  • mouseReleased()

The method names are self explanatory but the process may not be. We first create an instance of the MouseListener, which is an interface, and override these methods. Then we register our listener with the component. When the mouse is clicked on our component, the method mouseClicked() will be executed. When the mouse hovers on our component, the method mouseEntered() is executed and when the mouse moves away from our component, the mouseExited().

One last thing before we return to our example. How many from the mouse listener's methods do we need to use to fulfil our requirement? Only two, as the requirement is to change the background when the mouse hovers in and out of our component. Java provides what are referred to adaptors. WTF? Java provides a skeleton class that implement this interfaces and provide default methods (methods that do nothing) for all the interface methods. We can then extend this class and override the method we want. Since the adaptor overrides all the interface methods, we only have to override those we're interested in, which simplifies our work.

Back to our initListeners() method. We're creating an instance of MouseAdapter, and registering it with our component.

  private void initListeners() {
    addMouseListener(new MouseAdapter() {
      public void mouseExited(MouseEvent e) {
        setBackground(Color.WHITE);
        repaint();
      }

      public void mouseEntered(MouseEvent e) {
        setBackground(Color.RED);
        repaint();
      }
    });
  }

As you can see from the above code fragment, we're only overriding two methods, the mouseEntered() and mouseExited() methods. In these methods we're changing the background colour and calling the repaint method.

Straight forward he

Getting it to the next level

That was fun, but let's improve it. Let's draw (fill) a circle following the mouse motion.

Before we can do so, we have to discuss another thing, the MouseEvent.

Before the event is fired and our listeners' methods invoked, information about the event is gathered (by something else which you don't need to care about, at least for now) and forwarded to you (the developer) as an event object, the MouseEvent in this case. This object contains information pertaining to our event, such as the mouse position, the time of the event and other things which may be useful when processing such event. We can (need to) use such information to achieve our goal as shown below.

package com.albertattard.pwtm;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JComponent;

@SuppressWarnings("serial")
public class MyComponent extends JComponent {

  private Point mousePoint; 

  public MyComponent() {
    setForeground(Color.BLUE); 
    initListeners();
  }

  private void initListeners() {
    addMouseListener(new MouseAdapter() {
      public void mouseExited(MouseEvent e) {
        setBackground(Color.WHITE);
        repaint();
      }

      public void mouseEntered(MouseEvent e) {
        setBackground(Color.RED);
        repaint();
      }
    });

    addMouseMotionListener(new MouseMotionAdapter() {
      @Override
      public void mouseMoved(MouseEvent e) {
        mousePoint = e.getPoint();
        repaint();
      }
    });
  }

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    g.setColor(getBackground());
    g.fillRect(0, 0, getWidth(), getHeight());

    if (mousePoint != null) {
      int ballSize = 15;
      g.setColor(getForeground());
      g.fillOval(mousePoint.x - ballSize / 2, 
                 mousePoint.y - ballSize / 2,
                 ballSize, ballSize);
    }
  }
}

The changes from the previous class are highlighted and discussed below.

An instance field was added to our component. Why? We need to transfer the mouse position, magically provided to us by the mouse events (discussed later on), to the paint method. Since we cannot call the paint method directly from the event (the reapint() call is something else), we need to save this information in the instance field, mousePoint, and the use it from the paint method. This instance field is accessible by both methods. First this field is updated by the method

      public void mouseMoved(MouseEvent e) {
        mousePoint = e.getPoint();
        repaint();
      }

within our new adaptor, . The method mouseMoved() is invoked every time the mouse is moved within our component. This is important to understand. Only the mouse events that are related with our component are captured by our listeners. So when the mouse moves out of our component, this method will stop being invoked.

Next we'll discuss the mouse clicks

Handling the clicks

Let say that we also want to handle the mouse click that happen on our component. For simplicity we'll display a simple text in the top left corner listing the number of click that occure as shown below.

A long story short, add a new instance field (called clickCount) of type int and paint its value in the paint method as highlighted below. Also, override the appropriate method in the mouse adaptor.

package com.albertattard.pwtm;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;

import javax.swing.JComponent;

@SuppressWarnings("serial")
public class MyComponent extends JComponent {

  private Point mousePoint;
  private int clickCount; 

  public MyComponent() {
    setForeground(Color.BLUE);
    initListeners();
  }

  private void initListeners() {
    addMouseListener(new MouseAdapter() {
      public void mouseExited(MouseEvent e) {
        setBackground(Color.WHITE);
        repaint();
      }

      public void mouseEntered(MouseEvent e) {
        setBackground(Color.RED);
        repaint();
      }

      @Override
      public void mouseClicked(MouseEvent e) {
        clickCount++;
        repaint();
      }
    });

    addMouseMotionListener(new MouseMotionAdapter() {
      @Override
      public void mouseMoved(MouseEvent e) {
        mousePoint = e.getPoint();
        repaint();
      }
    });
  }

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    g.setColor(getBackground());
    g.fillRect(0, 0, getWidth(), getHeight());

    if (mousePoint != null) {
      int ballSize = 15;
      g.setColor(getForeground());
      g.fillOval(mousePoint.x - ballSize / 2,
          mousePoint.y - ballSize / 2, ballSize, ballSize);
    }

    if (clickCount > 0) {
      g.setColor(getForeground());
      g.drawString("Click Count: " + clickCount, 10, 20);
    } else {
      g.setColor(getForeground());
      g.drawString("No Clicks!!!", 10, 20);
    }
  }
}

The above code is not rocket science but it has a little flaw. If you drag the mouse inside the component, the blue circle will not follow the mouse pointer as illustrated below.

Why not? The MouseMotionListener, which the MouseMotionAdapter implements has two methods but we're only overriding one as illustrated below.

    addMouseMotionListener(new MouseMotionAdapter() {
      @Override
      public void mouseMoved(MouseEvent e) {
        mousePoint = e.getPoint();
        repaint();
      }
    });

The other method, mouseDragged(), is used to handle mouse dragging events as illustrated code fragment below.

     addMouseMotionListener(new MouseMotionAdapter() {
      @Override
      public void mouseDragged(MouseEvent e) {
        mousePoint = e.getPoint();
        repaint();
      }
      
      @Override
      public void mouseMoved(MouseEvent e) {
        mousePoint = e.getPoint();
        repaint();
      }
    }); 

Dragging is more complex than one may think and is discussed further in the following section

Dragging the mouse

For this example, we'll create a new component and will make use of it rather than keep changing the other one. Like before, we'll start with a simple example and keep building on it. We'll start with the following simple component.

package com.albertattard.pwtm;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JComponent;

@SuppressWarnings("serial")
public class MyDragComponent extends JComponent {

  private Rectangle draggableShape;

  public MyDragComponent() {
    draggableShape = new Rectangle(20, 20, 50, 50);
    setForeground(Color.BLUE);
  }

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    Graphics2D g2d = (Graphics2D) g;

    g2d.setColor(getBackground());
    g2d.fillRect(0, 0, getWidth(), getHeight());

    g2d.setColor(getForeground());
    g2d.fill(draggableShape);
  }
}

This component simply draws a solid blue 50 by 50 rectangle at the x and y coordinates 20 as illustrated below

Remember to add this component to the frame before you test it out

Let's start be arm our rectangle with a red border when the mouse enters the box as shown below. Note that the component has a red border and the mouse events are triggered for any mouse activity within the component. We need to filter out movements outside the rectangle and only arm the rectangle when the mouse is on it.

How can we do that? Quiet simple. Like before, we create a field (called mouseInside) and will set it to true when the mouse is within the rectangle and false otherwise. The updates are highlighted below.

package com.albertattard.pwtm;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;

import javax.swing.JComponent;

@SuppressWarnings("serial")
public class MyDragComponent extends JComponent {

  private Rectangle draggableShape;
  private boolean mouseInside; 

  public MyDragComponent() {
    draggableShape = new Rectangle(20, 20, 50, 50);
    setForeground(Color.BLUE);
    initListeners();
  }

  private void initListeners() {
    addMouseMotionListener(new MouseMotionAdapter() {
      @Override
      public void mouseMoved(MouseEvent e) {
        mouseInside = draggableShape.contains(e.getPoint());
        repaint();
      }
    });
  }

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    Graphics2D g2d = (Graphics2D) g;

    g2d.setColor(getBackground());
    g2d.fillRect(0, 0, getWidth(), getHeight());

    g2d.setColor(getForeground());
    g2d.fill(draggableShape);

    if (mouseInside) {
      g2d.setColor(Color.RED);
      g2d.setStroke(new BasicStroke(3));
      g2d.draw(draggableShape);
      g2d.setStroke(new BasicStroke(1));
    }
  }
}
Why are we resetting the stroke at the end of the paint component method? Ideally you rest all changes made to the graphics component once done as other paint methods for this object such paint border method will inherit your changes. In this case, if you do not reset the stroke the border will be painted with this stroke.

The following code fragment does the trick. The method mouseMoved () is continually called by the underlying system (so to say) every time the mouse is moved over our component. When the mouse point (retrieved from the mouse event provided as the method parameter) is within the rectangle area, the mouseInside field is set to true.

      public void mouseMoved(MouseEvent e) {
        mouseInside = draggableShape.contains(e.getPoint());
        repaint();
      }

Let's not stop here. Let's make the rectangle draggable. By draggable I mean that the user can drag the rectangle around our component as shown below.

This is not an easy task and some may find it hard to understand but let's not panic. The following code does the job.

package com.albertattard.pwtm;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JComponent;

@SuppressWarnings("serial")
public class MyDragComponent extends JComponent {

  private Rectangle draggableShape;
  private boolean mouseInside;
  private Point lastMousePoint; 

  public MyDragComponent() {
    draggableShape = new Rectangle(20, 20, 50, 50);
    setForeground(Color.BLUE);
    initListeners();
  }

  private void initListeners() {
    addMouseMotionListener(new MouseMotionAdapter() {
      @Override
      public void mouseMoved(MouseEvent e) {
        mouseInside = draggableShape.contains(e.getPoint());
        lastMousePoint = e.getPoint();
        repaint();
      }

      @Override
      public void mouseDragged(MouseEvent e) {
        mouseInside = draggableShape.contains(e.getPoint());
        if (mouseInside) {
          if (lastMousePoint != null) {
            draggableShape.x += 
                e.getPoint().x - lastMousePoint.x;
            draggableShape.y += 
                e.getPoint().y - lastMousePoint.y;
          }
        }
        lastMousePoint = e.getPoint();
        repaint();
      }
    });
  }

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    Graphics2D g2d = (Graphics2D) g;

    g2d.setColor(getBackground());
    g2d.fillRect(0, 0, getWidth(), getHeight());

    g2d.setColor(getForeground());
    g2d.fill(draggableShape);

    if (mouseInside) {
      g2d.setColor(Color.RED);
      g2d.setStroke(new BasicStroke(3));
      g2d.draw(draggableShape);
      g2d.setStroke(new BasicStroke(1));
    }
  }
}

Let's starts discussing the code.

When the mouse is dragged within the rectangle, we're also moving it. The rectangle can be dragged from anywhere within. So we have to take under consideration where the mouse was clicked within the rectangle and shift the rectangle by the offset, that is, by the amount the mouse was moved. We cannot simply set the rectangle's x and y coordinates to match those of the event. The following sketch highlights this (hopefully).

To do this we added a new field called lastMousePoint, which is used to save the last mouse position (as the name infers). Instead of setting the rectangle coordinates equal to the mouse coordinates we adjust them by how much the mouse was moved using the following (copy and paste from above) logic.

      @Override
      public void mouseDragged(MouseEvent e) {
        mouseInside = draggableShape.contains(e.getPoint());
        if (mouseInside) {
          if (lastMousePoint != null) {
            draggableShape.x += 
                e.getPoint().x - lastMousePoint.x;
            draggableShape.y += 
                e.getPoint().y - lastMousePoint.y; 
          }
        }
        lastMousePoint = e.getPoint();
        repaint();
      }

It is important to also update the lastMousePoint field before we exit the above method.

A tip for the champions. If you observe the previous class, you notice that the field lastMousePoint is only used within the adaptor. Ideally we move this field within the adaptor as highlighted below.

 addMouseMotionListener(new MouseMotionAdapter() {
      
      private Point lastMousePoint; 
      
      @Override
      public void mouseMoved(MouseEvent e) {
        mouseInside = draggableShape.contains(e.getPoint());
        lastMousePoint = e.getPoint();
        repaint();
      }

      @Override
      public void mouseDragged(MouseEvent e) {
        mouseInside = draggableShape.contains(e.getPoint());
        if (mouseInside) {
          if (lastMousePoint != null) {
            draggableShape.x += 
                e.getPoint().x - lastMousePoint.x;
            draggableShape.y += 
                e.getPoint().y - lastMousePoint.y;
          }
        }
        lastMousePoint = e.getPoint();
        repaint();
      }
    });

Too keep it simple, the above change will not apply to the rest of the examples

One last thing before we go. Note that we're able to drag the rectangle outside the component printable area. This is not always good and we would like to prevent this as illustrated below.

This is not a difficult change. All we need to do is cap the changes to the rectangle as illustrated in the following code fragment.

      @Override
      public void mouseDragged(MouseEvent e) {
        mouseInside = draggableShape.contains(e.getPoint());
        if (mouseInside) {
          if (lastMousePoint != null) {
            draggableShape.x += 
                e.getPoint().x - lastMousePoint.x;
            draggableShape.y += 
                e.getPoint().y - lastMousePoint.y;

            draggableShape.x = Math.max(draggableShape.x, 0);
            draggableShape.x = Math.min(draggableShape.x,
                getWidth() - draggableShape.width);
            draggableShape.y = Math.max(draggableShape.y, 0);
            draggableShape.y = Math.min(draggableShape.y,
                getHeight() - draggableShape.height);
          }
        }
        lastMousePoint = e.getPoint();
        repaint();
      }

The rest of the class is not listed for brevity.

Tuesday, 13 October 2009

Practical Example of Simple Java UI Component

In this article we'll discuss how to create a simple Java graphical component such as a javax.swing.JButton (without going into details of event handling) and include it within an application. Our component will display (paint) a flat rectangle of a set colour as illustrated below.

We'll be making use of two classes one representing the simple custom component and the other will be mounting the frame and including our component in it. All classes discussed here are saved under the package "com.albertattard.cgui". You can change this to your own package. In this article we'll only be exploring the creation of simple custom UI component. Many other related topics will be only mentioned here but not discussed. While you are encouraged to explore all the topics that are only mentioned (but not discussed) here, one must be cautious not to overwhelm him/herself in the process.

Custom UI Component

All Java Swing (don't worry if you don't know with Swing means but if you insist here is a link which may answer some of your questions: http://java.sun.com/docs/books/tutorial/uiswing/) graphical components inherit from the javax.swing.JComponent abstract class. Our component is no exception. So let's start by creating a simple class called: FlatRectComponent. This class will be extending the JComponent class mentioned earlier and will override the paintComponent() method as highlighted in the example below.

package com.albertattard.cgui;

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;

@SuppressWarnings("serial")
public class FlatRectComponent extends JComponent {
  @Override
  protected void paintComponent(Graphics g) {
    // Clear the previous screen
    super.paintComponent(g);
    // Set the painting colour to magenta
    g.setColor(Color.MAGENTA);
    // Draw a solid rectangle at coordinates x and y set to 
    // 10px with width and height set to 100px
    g.fillRect(10, 10, 100, 100);
  }
}
Why did we override the paintComponent() method? All UI programs are responsible from painting themselves on the screen. For example this article you're reading is an image displayed on your screen (unless you printed it on a piece of paper). The browser downloaded this article as HTML and transformed (or better rendered) it as an image. All other applications do the same. For example a button, is a simple set of paint instructions which makes it look real (as you see it on the screen). Also events, such as mouse hovering, may change the way the button or any other graphical component looks making the graphics interacting with the user inputs.

In the case of Java, all graphical components are responsible of painting themselves by overriding the paintComponent() method and paint on the provided java.awt.Graphics. In the above example, we're simply painting a flat rectangle in magenta. Then this method is called by some other class which displays it on the screen as you see it. You don't have to worry about how or what invokes this method. Just remember that you cannot call it yourself.

Alone this class is not of much use. But since it's inheriting from the JComponent class, we can include it with other Java Swing components such as javax.swing.JFrame.

Including it in a JFrame

Let's create a simple frame which will host our new component. For simplicity, we're not going to discuss Layout Managers. We're going to use the absolute layout where we specify the location and size for every component. An example of our frame class is shown below.

package com.albertattard.cgui;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class FrameDemo extends JFrame {

  private static void createFrame() {
    JFrame frame = new FrameDemo();
    frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
    frame.setTitle("Custom UI Demo");
    frame.setSize(600, 400);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        createFrame();
      }
    });
  }

  private FrameDemo() {
    // Set the layout of this frame to absolute layout
    setLayout(null);

    // Create the flat rectangle component and add it to 
    // the frame
    FlatRectComponent comp = new FlatRectComponent();
    comp.setBounds(0, 0, 200, 200);
    add(comp);
  }
}
The above class contains two static methods and a constructor. Let's analyse these further starting from the main() method. The main() method is the first method called by the JVM. This is from where the application starts. This method calls the other static method createFrame() by scheduling a job on the AWT event dispatching thread. This sounds as complex as rocket science but you have nothing to be afraid from. For now all you have to do is copy and paste it. The static method createFrame() creates an instance of our frame class, FrameDemo and set the frame properties, such as title, size and location.

The constructor of this class starts by setting the layout manager to absolute layout. Then it creates an instance of our components and set its size and location accordingly. The setBounds() method takes four parameters, the first two represent the location as x and y coordinate while the latter to represent the size. Finally it adds this component to its container (don't worry about containers for now - this is the fort time I'm asking to accept something as is without having to dig any further). This is a very important step. Many forget to add the components and then wonder why these don't appear in the frame.

How many instances of our component can we include in the frame? As many as you want. In practice there's a limit which is dependent on the amount of memory available to your application, but you don't have to worry about that neither as our application is fairly simple and consume very little memory. Let's amend the previous frame class and include another instance of the FlatRectComponent UI component. All we need to do is create another instance and adding this instance to the frame's container. We also need to set the bounds of the new component such that this don't conflict with the previous one as otherwise we may have a graphical issue. The following code fragment highlights the changes made to the frame class. Only the constructor was changed, the other methods are unchanged and thus not shown here. Also, note that a border was added to the components to visualise the component perimeter.

  private FrameDemo() {
    setLayout(null);

    // The first component
    FlatRectComponent comp1 = new FlatRectComponent();
    comp1.setBounds(0, 0, 200, 200);
    comp1.setBorder(
      BorderFactory.createLineBorder(Color.BLACK, 1));
    add(comp1);

    // The second component
    FlatRectComponent comp2 = new FlatRectComponent();
    comp2.setBounds(250, 50, 200, 200);
    comp2.setBorder(
      BorderFactory.createLineBorder(Color.BLACK, 1));
    add(comp2); 
  }
The program should now display two, identical, components as illustrated below.

Customising the Custom UI Component

As is, our component is not much of use as we cannot customise it. The painComponent() method make use of literals and constants and so our component will always look like that. In order to be able customise it we need to add some states to our custom UI component.

What are these? States in objects are instance fields, or as some call them: class variables. The instance fields are used to save the state of the object. The state of the object is all information about the object. For example our custom UI component displays a rectangle. This rectangle needs to have an x and y coordinates and a size. A rectangle cannot exist without these states (properties). Try to draw a rectangle on a piece of paper yourself without making use of the mentioned states. You'll notice that you cannot as, as soon as you draw it, you have provided the x, y and size states. In this case we need to capture these states in some instance fields. The same applies for the rectangle colour.

We can start by adding an instance of the java.awt.Rectangle class and save our rectangle states (size and location) there. Alternatively, we can create four integer variables which represent the x, y, width and height values for our rectangle. Even though it may sound more complex I suggest we go with the first option as this give us other functionality already available in the Rectangle class, which otherwise we have to program, such as the translate() method.

package com.albertattard.cgui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JComponent;

@SuppressWarnings("serial")
public class FlatRectComponent extends JComponent {

  private Rectangle rect = new Rectangle(10, 10, 100, 100); 

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    // Make use of the 2D version of the Graphics component
    Graphics2D g2D = (Graphics2D) g; 

    g2D.setColor(Color.MAGENTA);
    g2D.fill(rect); 
  }
}
In the above example we've changed the way the rectangle is painted. Before we used literals, while now we're painting an instance of Rectangle. Also, now we're using a java.awt.Graphics2D component instead of the Graphics component. We're not going to go into much detail about this here as otherwise we'll be ending up writing a book. But in a nutshell, the Graphics2D inherits from the Graphics and adds more functionality (methods) which we can use. The object invoking out paintComponent() method (which we agreed not to discuss here) will be passing an instance of java.awt.Graphics2D to the paintComponent() method. All we're doing is recasting it back to its original form. Don't worry about this for now. All you need to know is that you should cast your Graphics to Graphics2D.

So far we have not yet provided any means (methods) which allow the rectangle instance field, rect, to be changed. That is, there is no way how one can change the x, y and size values for this instance. Let's add four methods, which can be used to set the any of the rectangle states.

package com.albertattard.cgui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;

import javax.swing.JComponent;

@SuppressWarnings("serial")
public class FlatRectComponent extends JComponent {

  private Rectangle rect = new Rectangle(10, 10, 100, 100);

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    Graphics2D g2D = (Graphics2D) g;

    g2D.setColor(Color.MAGENTA);
    g2D.fill(rect);
  }

  public void setHeight(int height) {
    rect.height = height;
    repaint();
  }

  public void setWidth(int width) {
    rect.width = width;
    repaint();
  }

  public void setX(int x) {
    rect.x = x;
    repaint();
  }

  public void setY(int y) {
    rect.y = y;
    repaint();
  }
}
Note that at the end of every setXXX() method we're calling the repaint() method. All we're doing here is instructing the frame's container (this is the guy which is invoking our paintComponent() method) to repaint this component. The frame's container will in turn call the paintComponent() method for this component and providing the appropriate information. It is important to do so after every change as otherwise your changes will not immediately appear. But these will appear only when the frame completely repaints all its components. Remember that you cannot call the paintComponent() yourself. All you can do is call the repaint() method.

So far we've only provided a means to customise our component. But we have not yet customized it. Let's do that. We can customise our component by calling the setXXX() from the frame class as highlighted bold below. The following code fragment only shows the constructor of the frame class as the other methods are unchanged.

  private FrameDemo() {
    setLayout(null);

    // The first component
    FlatRectComponent comp1 = new FlatRectComponent();
    comp1.setBounds(0, 0, 200, 200);
    comp1.setX(5);
    comp1.setY(5);
    comp1.setWidth(190);
    comp1.setHeight(190); 
    comp1.setBorder(
      BorderFactory.createLineBorder(Color.BLACK, 1));
    add(comp1);

    // The second component
    FlatRectComponent comp2 = new FlatRectComponent();
    comp2.setBounds(250, 50, 200, 200);
    comp2.setX(50);
    comp2.setY(50);
    comp2.setWidth(100);
    comp2.setHeight(100); 
    comp2.setBorder(
      BorderFactory.createLineBorder(Color.BLACK, 1));
    add(comp2);
  }
The above program should produce the following outcome.

Note that now the coloured rectangle size and location can be varied from the setter methods provided. This makes our component customizable. We can also change the paint colour by including an instance field representing the colour and a method in our custom UI component which changes the colour as illustrated below.

// imports not shown here 
public class FlatRectComponent extends JComponent {
  private Color rectColour = Color.MAGENTA; 

  // other field and methods are not show for brevity

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    Graphics2D g2D = (Graphics2D) g;

    g2D.setColor(rectColour); 
    g2D.fill(rect);
  }

  public void setRectangleColour(Color rectColour) {
    this.rectColour = rectColour;
    repaint();
  }
}
All we need to do now is call this method and change the colour accordingly as highlighted below.

  private FrameDemo() {
    setLayout(null);

    // The first component
    FlatRectComponent comp1 = new FlatRectComponent();
    comp1.setBounds(0, 0, 200, 200);
    comp1.setX(5);
    comp1.setY(5);
    comp1.setWidth(190);
    comp1.setHeight(190);
    comp1.setRectangleColour(Color.GREEN); 
    comp1.setBorder(
      BorderFactory.createLineBorder(Color.BLACK, 1));
    add(comp1);

    // The second component
    FlatRectComponent comp2 = new FlatRectComponent();
    comp2.setBounds(250, 50, 200, 200);
    comp2.setX(50);
    comp2.setY(50);
    comp2.setWidth(100);
    comp2.setHeight(100);
    comp2.setRectangleColour(Color.CYAN); 
    comp2.setBorder(
      BorderFactory.createLineBorder(Color.BLACK, 1));
    add(comp2);
  }

The following screenshot shows the final version of our program.

Conclusion

In this article we've saw how to create a simple custom Java UI component and display a simple flat rectangle. We've also added methods which can be used to customise this component.

Sunday, 28 September 2008

Practical Example of Swing Timer

If you have to do something repetitively, what would you use? The first quick answer would be: loops (iteration). But that would prevent other things from happening until the loop is done! Threads can solve the seizing of control problem. But threads may be complex for someone who is new to programming and require some skill. What else can we use? Swing Timer!

Swing Timer

Swing timer provides a unique wrapper which presents threads in an event driven fashion. Imagine we need to display the text "hello" in the console every one second without having to seize the control. How would we do that using the swing timer class?

Note that there are three Timer classes in the Java API. We're using javax.swing.Timer. Make sure you have the correct import.


ActionListener listener = new ActionListener(){
  public void actionPerformed(ActionEvent event){
    System.out.println("hello");
  }
};
Timer displayTimer = new Timer(1000, listener);
displayTimer.start();

The swing timer has only one constructor which takes two parameters: the time gap (sometimes referred to as delay, in milliseconds) and an action listener. The swing timer fires an action event at specific intervals, which is handled by the action listener. The interval can be adjusted and more listeners can be added (and removed) as needs be using the setDelay(int) and addActionListener(ActionListener) (and removeActionListener(ActionListener)) methods. But let's keep it simple. For more details about available functionality provided by the swing timer class please refer to the javax.swing.Timer API.

Start the timer

No action will happen until the start() method is invoked. When the start method is invoked the timer waits for some time (referred to as initial delay) before it fires the first action event. This is governed by the initial delay which is initially set equal to the time gap provided as the constructor's first parameter. The initial delay can be adjusted accordingly using the setInitialDelay(int) method. It's important to set this value before invoking the start method.


Timer displayTimer = new Timer(1000, listener);
displayTimer.setinitialDelay(0);
displayTimer.start();

The above example will fire the first action immediately, as the initial delay is set to 0 milliseconds. It will trigger subsequent events with a one second (1000 milliseconds) interval.

Stop the timer

One of the best features provided by the swing timer is the ability to start and stop and even restart the timer without having to worry about the thread lifecycle.

Those who already worked with threads know that a thread cannot be restarted. Once ready, it must die (unreferenced) and another instance has to be instantiated in order to perform the thread's work.

To stop the swing timer, simple call the stop() method. This will stop the swing timer from firing any subsequent action events.


displayTimer.stop();

The swing timer can be restarted by simply calling the start() method again.

Restarting the swing timer

The swing timer can be restarted using the stop() start() methods. Alternatively, the restart() method can be used which cancels any pending firings of events and starts all over.


displayTimer.restart();

A graphical example

Let's create a simple applet which has a bouncing ball. The ball will stop moving once the user presses any key and starts bouncing again once another key is pressed again. This example will toggle the state of the swing timer between running and stopped. The swing timer provides a method called isRunning() which returns true if the swing timer is running, false otherwise.


    addKeyListener(new KeyAdapter() {
      @Override
      public void keyPressed(KeyEvent e) {
        if (moveBallTimer.isRunning()) {
          moveBallTimer.stop();
        } else {
          moveBallTimer.start();

        }
      }
    });

The full applet code listed below


import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;

import javax.swing.JApplet;
import javax.swing.Timer;

public class SwingTimerAppletDemo1 extends JApplet {

  private static final long serialVersionUID = 656209471758159755L;

  private Ellipse2D ball;
  private Timer moveBallTimer;
  private int moveX;
  private int moveY;

  @Override
  public void init() {
    ball = new Ellipse2D.Double(0, 0, 10, 10);
    moveX = 5;
    moveY = 5;

    moveBallTimer = new Timer(100, new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        moveBall();
        repaint();
      }
    });

    addKeyListener(new KeyAdapter() {
      @Override
      public void keyPressed(KeyEvent e) {
        if (moveBallTimer.isRunning()) {
          moveBallTimer.stop();
        } else {
          moveBallTimer.start();

        }
      }
    });
  }

  protected void moveBall() {
    int width = getWidth();
    int height = getHeight();

    Rectangle ballBounds = ball.getBounds();
    if (ballBounds.x + moveX < 0) {
      moveX = 5;
    } else if (ballBounds.x + ballBounds.width + moveX > width) {
      moveX = -5;
    }
    if (ballBounds.y + moveY < 0) {
      moveY = 5;
    } else if (ballBounds.y + ballBounds.height + moveY > height) {
      moveY = -5;
    }
    ballBounds.x += moveX;
    ballBounds.y += moveY;

    ball.setFrame(ballBounds);
  }

  @Override
  public void paint(Graphics g) {
    super.paint(g);

    Graphics2D g2d = (Graphics2D) g;
    g2d.setColor(Color.RED);
    g2d.fill(ball);
  }

  @Override
  public void start() {
    moveBallTimer.start();
  }

  @Override
  public void stop() {
    moveBallTimer.stop();
  }
}

The above program is mainly doing two things: listens for user input through the keyboard and moving/bouncing the ball around the applet. This was possible as the swing timer makes use of threads to fire an action event and wait until the delay is over to fire the next action event. While waiting, the application can do other things such as listens to the user input.

What will happen if an action event is fired before the previous one has finish execution? By default the swing timer will merge consecutive action events into one. This behaviour is determined by the coalesce property. If set to false using the setCoalesce(boolean) method, the swing timer will fire all action event. This will cause queuing of action events as these are triggered at a faster rate than are handled.

Conclusion

The swing timer is a good candidate for invoking repetitive tasks without seizing the control. On the other hand, the tasks cannot be long lasting ones as otherwise it may block other things from happening making the application less responsive. The swing worker should be used for long lasting jobs.

Thursday, 4 September 2008

Practical Example of Swing Worker

Please note that this page has moved to: http://www.javacreed.com/swing-worker-example/.

Java provides a neat way to carry out long lasting jobs without have to worry about threads and hanging the application. It's called SwingWorker. It's not the latest thing on Earth (released with Java 1.6) and you may have already read about it. What I never came across was a practical example of the swing worker.

Swing Worker

SwingWorker is an abstract class which hides the threading complexities from the developer. It's an excellent candidate for applications that are required to do tasks (such as retrieving information over the network/internet) which may take some time to finish. It's ideal to detach such tasks from the application and simply keep an eye on their progress. But before we hit the road and start working with the swing worker we have to see what "eye" are we going to put on our worker so to say.

The following example illustrates a simple empty worker that will return/evaluate to an integer when the given task is finished. It will inform the application (the "eye" thing) with what's happening using objects of type string, basically text messages.


import javax.swing.SwingWorker;
 
public class MyBlankWorker extends SwingWorker<Integer, String> {
 
  // Some code must go here
 
}

The string worker class provides two place holders (generics). The first one represents the type of object returned when the worker has finished working. The second one represents the type of information that the worker will use to inform (update) the application with its progress. The swing worker class also provides means to update the progress by means of an integer which has nothing to do with the two generics mentioned before.

Practical Example

Example: Let say we need to find the number of occurrences of a given word with in some documents. So we would be writing something like:

 
import java.io.File;
import javax.swing.SwingWorker;
 
public class SearchForWordWorker 
             extends SwingWorker<Integer, String> {
 
  private final String word;
  private final File[] documents;
 
  public SearchForWordWorker(String word, File[] documents){
    this.word = word;
    this.documents = documents;
  }
 
  @Override
  protected Integer doInBackground() throws Exception {
    int matches = 0;
    for(int i=0, size=documents.length; i<size; i++){
      // Update the status: the keep an eye on thing
      publish("Searching file: "+documents[i]);
 
      try {
        // Do the search stuff
        // Here you increment the variable matches
      } finally {
        // Close the current file
      }
 
      // update the progress
      setProgress( (i+1) * 100 / size);
    }
 
    return matches;
  }
}

The first thing that comes into mind is where is the text "Searching file: ..." is going? The swing worker class provides another method called process which accepts a list (of type string in our case) and used to process the published information (which can be an object of any kind). Overriding this method, allows us to take full control of this information.


  @Override
  protected void process(List<String> chunks){
    for(String message : chunks){
      System.out.println(message);
    }
  }

The above example is not much useful. We may need to update the status bar of an application or the text of the progress bar or a label sitting somewhere in the application. Since we may be monitoring this worker from various UI components, ideally we add a level of isolation between the worker and the UI components. In many examples, the worker was fed UI components as constructor parameters. I would go for interfaces instead to make the design pluggable when possible.

In other occasions a worker may be used to populate a table which information is coming from a slow source. In this case we may use the table model as one of the workers parameter and the array of objects representing the row. The following example makes use of the default table model as the design is simpler.

 
import java.util.List;
import javax.swing.SwingWorker;
import javax.swing.table.DefaultTableModel;
 
public class PopulateTableWorker 
             extends SwingWorker<DefaultTableModel, Object[]> {
 
  private final DefaultTableModel model;
 
  public PopulateTableWorker(DefaultTableModel model){
    this.model = model;
  }
 
  @Override
  protected DefaultTableModel doInBackground() throws Exception {
    // While there are more rows
    while(Math.random() < 0.5){
      // Get the row from the slow source
      Object[] row = {1, "Java"};
      Thread.sleep(2000);
 
      // Update the model with the new row
      publish(row);
    }
 
    return model;
  }
 
  @Override
  protected void process(List<Object[]> chunks){
    for(Object[] row : chunks){
      model.addRow(row);
    }
  }
}

Note that the table model interface does not support addition of new rows. Alternatively we can make use of the table model interface. The model must be able to add a new row when this is required as otherwise an exception may be thrown.


import java.util.List;
import javax.swing.SwingWorker;
import javax.swing.table.TableModel;
 
public class PopulateTableWorker 
             extends SwingWorker<TableModel, Object[]> {
 
  private final TableModel model;
  private int rowIndex = 0;
 
  public PopulateTableWorker(TableModel model){
    this.model = model;
  }
 
  @Override
  protected TableModel doInBackground() throws Exception {
    // While there are more rows
    while(Math.random() < 0.5){
      // Get the row from the slow source
      Object[] row = {1, "Java"};
      Thread.sleep(2000);
 
      // Update the model with the new row
      publish(row);
    }
 
    return model;
  }
 
  @Override
  protected void process(List<Object[]> chunks){
    for(Object[] row : chunks){
      for(int columnIndex=0, size=row.length; 
                                      columnIndex<size; 
                                      columnIndex++){
        model.setValueAt(row[columnIndex], rowIndex, columnIndex);
      }
    }
    rowIndex++;
  }
}

Back to our example, we can have an interface called Informable (or you can pick a name of your liking) with one method: messageChanged(String message), which will be invoked whenever some progress is made and published.

 
public interface Informable {
    void messageChanged(String message);
}

The worker also requires an instance of the interface as it will be used to publish the results through.

 
import java.io.File;
import java.util.List;
import javax.swing.SwingWorker;
 
public class SearchForWordWorker 
             extends SwingWorker<Integer, String> {
 
  private final String word;
  private final File[] documents;
  private final Informable informable;
 
  public SearchForWordWorker(String word,
                                File[] documents,
                                Informable informable){
    this.word = word;
    this.documents = documents;
    this.informable = informable;
  }
 
  @Override
  protected Integer doInBackground() throws Exception {
    int matches = 0;
    for(int i=0, size=documents.length; i<size; i++){
      // Update the status: the keep an eye on thing
      publish("Searching file: "+documents[i]);
 
      try {
        // Do the search stuff
        // Here you increment the variable matches
      } finally {
        // Close the current file
      }
 
      // update the progress
      setProgress( (i+1) * 100 / size);
    }
 
    return matches;
  }
 
  @Override
  protected void process(List<String> chunks){
    for(String message : chunks){
      informable.messageChanged(message);
    }
  }
}

The above worker can be easily plugged into the application as show in the following example. This application has three graphical components: a label acting as a title displaying the latest message published by the worker; a text area which displays all messages published by the worker; and a progress bar showing the progress made.

Demo: Swing Worker Application

The label and text area are governed by Informable interface. The progress bar is governed by the worker's progress property.

 
import java.awt.*;
import java.beans.*;
import java.io.*;
import javax.swing.*;
 
public class Application extends JFrame {
 
  // The UI Components
  private JLabel label;
  private JProgressBar progressBar;
  private JTextArea textArea;
 
  private void initComponents(){
    // The interface will update the text of the UI components
    Informable informable = new Informable(){
      @Override
      public void messageChanged(String message){
        label.setText(message);
        textArea.append(message + "\n");
      }
    };
 
    // The UI components
    label = new JLabel("");
    add(label, BorderLayout.NORTH);

    textArea = new JTextArea(5, 30);
    add(new JScrollPane(textArea), BorderLayout.CENTER);
 
    progressBar = new JProgressBar();
    progressBar.setStringPainted(true);
    add(progressBar, BorderLayout.SOUTH);
 
    //  The worker parameters
    String word = "hello";
    File[] documents = {new File("Application.java"),
                        new File("Informable.java"),
                        new File("SearchForWordWorker.java")};
 
    // The worker
    SearchForWordWorker worker =
           new SearchForWordWorker(word, documents, informable){
       // This method is invoked when the worker is finished
       // its task
      @Override
      protected void done(){
        try {
          // Get the number of matches. Note that the 
          // method get will throw any exception thrown 
          // during the execution of the worker.
          int matches = get();
          label.setText("Found: "+matches);
 
          textArea.append("Done\n");
          textArea.append("Matches Found: "+matches+"\n");

          progressBar.setVisible(false);
        }catch(Exception e){
          JOptionPane.showMessageDialog(Application.this, 
                        "Error", "Search", 
                        JOptionPane.ERROR_MESSAGE);
        }
      }
    };

    // A property listener used to update the progress bar
    PropertyChangeListener listener = 
                               new PropertyChangeListener(){
      public void propertyChange(PropertyChangeEvent event) {
        if ("progress".equals(event.getPropertyName())) {
          progressBar.setValue( (Integer)event.getNewValue() );
        }
      }
    };
    worker.addPropertyChangeListener(listener);
 
    // Start the worker. Note that control is 
    // returned immediately
    worker.execute();
  }
 
  // The main method
  public static void main(String[] args){
    SwingUtilities.invokeLater(new Runnable(){
      public void run(){
        Application app = new Application();
        app.initComponents();
        app.setDefaultCloseOperation(EXIT_ON_CLOSE);
        app.pack();
        app.setVisible(true);
      }
    });
  }
}

The above example builds the application and kicks off the worker. The worker updates the application through the Informable instance. While the worker is performing it task, the application can carry on doing other things (event listening and painting) without hanging.

Cancel the Worker

Can we cancel the task? Yes. The worker can be stopped or better cancelled. The worker provides a method called cancel which accepts a parameter of type boolean. This parameter determines whether or not the worker should be waked up should it be found sleeping.

 
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import javax.swing.*;
 
public class Application extends JFrame {
 
  private JLabel label;
  private JProgressBar progressBar;
  private JTextArea textArea;
  private JButton  button;
  private SearchForWordWorker worker;
 
  private void initComponents(){
    // The interface will update the text of the UI components
    Informable informable = new Informable(){
      @Override
      public void messageChanged(String message){
        label.setText(message);
        textArea.append(message + "\n");
      }
    };
 
    // The UI components
    label = new JLabel("");
    add(label, BorderLayout.NORTH);
 
    textArea = new JTextArea(5, 30);
    add(new JScrollPane(textArea), BorderLayout.CENTER);

    // The cancel button 
    button = new JButton("STOP");
    button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        // Cancel the worker and wake it up should it be sleeping
        worker.cancel(true);
      }
    });
    add(button, BorderLayout.EAST);
 
    progressBar = new JProgressBar();
    progressBar.setStringPainted(true);
        add(progressBar, BorderLayout.SOUTH);
 
    //  The worker parameters
    String word = "hello";
    File[] documents = {new File("Application.java"),
                        new File("Informable.java"),
                        new File("SearchForWordWorker.java")};
                        
    // The worker
    worker = new SearchForWordWorker(word, documents, informable){
      @Override
      protected void done(){
        try {
          int matches = get();
          label.setText("Found: "+matches);

          textArea.append("Done\n");
          textArea.append("Matches Found: "+matches+"\n");
 
          progressBar.setVisible(false);
        }catch(Exception e){
          JOptionPane.showMessageDialog(Application.this, 
                       "Error", "Search", 
                       JOptionPane.ERROR_MESSAGE);
        }
      }
    };

    // A property listener used to update the progress bar
    PropertyChangeListener listener = 
                               new PropertyChangeListener(){
      public void propertyChange(PropertyChangeEvent event) {
        if ("progress".equals(event.getPropertyName())) {
          progressBar.setValue( (Integer)event.getNewValue() );
        }
      }
    };
    worker.addPropertyChangeListener(listener);
    
    // Start the worker. Note that control is 
    // returned immediately
    worker.execute();
  }
 
  public static void main(String[] args){
    SwingUtilities.invokeLater(new Runnable(){
      public void run(){
        Application app = new Application();
        app.initComponents();
        app.setDefaultCloseOperation(EXIT_ON_CLOSE);
        app.pack();
        app.setVisible(true);
      }
    });
  }
}

This will cause the worker's get method to throw the exception CancellationException to indicate that the worker was forced cancellation.

Conclusion

One thing to take from this article is: when possible do not refer to UI components directly from the worker. The worker is better viewed as the subject of the observer pattern. Provide an interface which the worker will use to communicate with its owner (application).