OOPDA BLUEJ CHAPTER 11

Terms and concepts: constructing GUIs, interface components, GUI layout, event, event handling, event listener, anonymous inner classes, modal dialog, class JFrame, method getContentPane, class JLabel, class JButton, class JMenuBar, class JMenu, class JMenuItem, class ActionEvent, class Color, class FlowLayout, class BorderLayout, class GridLayout, class BoxLayout, class Box, class JOptionPane, class EtchedBorder, class EmptyBorder,

component: buttons, labels, menus, menu items, checkboxes, sliders, text fields

layout: where components are placed, how components are arranged

event handling: user input from clicking button, typing keys, selecting menu items

image viewer and manipulation program

a graphics program has one or more JFrame windows (frames)

a frame has a title bar, a menu bar (optional), and a content pane

BlueJ: projects | chapter11 | imageviewer0-1
chapter11/imageviewer0-1/ImageViewer.java
chapter11/imageviewer0-1/README.TXT

adding a menu: class JMenuBar, class JMenu, class JMenuItem,

JMenuBar menubar = new JMenuBar();
frame.setJMenuBar(menubar);

JMenu fileMenu = new JMenu("File");
menubar.add(fileMenu);

JMenuItem openItem = new JMenuItem("Open");
fileMenu.add(openItem);

JMenuItem quitItem = new JMenuItem("Quit");
fileMenu.add(quitItem);

Events and Event Listeners

button click and menu selection generate an action event, mouse move and mouse click generate an mouse event, closing or minimizing a window generates a window event

listener objects register themselves with buttons, menu items, and windows to be notified about their events

to make an object a menu item listener

whenever the menu item is selected, the actionPerformed method in the listener object is called

BlueJ: projects | chapter11 | imageviewer0-2
chapter11/imageviewer0-2/ImageViewer.java
chapter11/imageviewer0-2/README.TXT

it is not good to have one listener object for everything because the actionPerformed method must figure out (with tedious hard-to-maintain code) which button, menu item, etc., generated the event

much better is having a dedicated listener object for each button, menu item, etc.

Inner and Anonymous Classes

inner classes make this easier to do

class ImageViewer {
   ...
   class OpenActionerListener implements ActionListener {
      public void actionPerformed(ActionEvent event) { ... }
   }
   class QuitActionerListener implements ActionListener {
      public void actionPerformed(ActionEvent event) { ... }
   }
   ...
   JMenuItem openItem = new JMenuItem("Open");
   openItem.addActionListener(new OpenActionerListener());
   ...
   JMenuItem quitItem = new JMenuItem("Quit");
   quitItem.addActionListener(new QuitActionerListener());
   ...
}

inner class method code has access to fields and methods in the enclosing (outer) class

anonymous inner classes let you bypass having to pick names for your inner classes at the expense of code that is harder to read

we define an anonymous inner class and create a single object from it in one statement using an interface type (or abstract class) and overriding the interface's (or abstract class's) abstract method(s)

class ImageViewer {
   ...
   JMenuItem openItem = new JMenuItem("Open");
   openItem.addActionListener(
      new ActionListener() {
         public void actionPerformed(ActionEvent e) { ... }
      }
   );
   ...
   JMenuItem quitItem = new JMenuItem("Quit");
   quitItem.addActionListener(
      new ActionListener() {
         public void actionPerformed(ActionEvent e) { ... }
      }
   );
   ...
}

an anonymous inner class defined and used in a method's code has access to the outer class's fields and methods and to the local variables and formal parameters of the method (local variables and formal parameters so accessed must be declared final).

...
for(Iterator it=filters.iterator(); it.hasNext(); ) {
   final Filter filter = (Filter)it.next();
   item = new JMenuItem(filter.getName());
   item.addActionListener(
      new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            applyFilter(filter);
         }
      }
   );
   menu.add(item);
}
...

anonymous inner classes help us easily create a dedicated listener object for each button, menu item, etc.

BlueJ: projects | chapter11 | imageviewer0-3
chapter11/imageviewer0-3/ImageViewer.java
chapter11/imageviewer0-3/README.TXT

OFImage represents an image (two-dimensional array of pixels) loaded from a file

ImageFileManager is for loading a JPEG file into an OFImage object and saving an OFImage object into a JPEG file

ImagePanel is a customized (extends) JComponent (the latter is the parent of JPanel) in which an image will be displayed

BlueJ: projects | chapter11 | imageviewer0-4
chapter11/imageviewer0-4/ImageFileManager.java
chapter11/imageviewer0-4/ImagePanel.java
chapter11/imageviewer0-4/ImageViewer.java
chapter11/imageviewer0-4/OFImage.java
chapter11/imageviewer0-4/README.TXT

Layout Managers

the layout manager in effect for a container or content pane determines how buttons, labels, etc., appear in the container (Jpanel) or content pane (what a JFrame's getContentPane method returns)

Each container or content pane has a default layout manager, which can be easily changed

FlowLayout, BorderLayout, GridLayout, BoxLayout

flow layout places components in a container in the order added, left to right, top to bottom, using the preferred size of each component, as many as will fit left to right in the container's size

if the container is resized by the user, its components are rearranged, left to right, top to bottom, using the preferred size of each component, as many as will fit left to right in the container's new size (see Figure 11.5 on page 317)

border layout divides a container into five regions, called north, south, east, west, and center

at most one component may be placed into a region

if the container is resized: the center's component is stretched or shrunk in both dimensions, the east and west components are stretched or shrunk in height but keep their preferred width, the north and south components are stretched or shrunk in width but keep their preferred height (see Figure 11.6 on page 318)

these two are the most commonly used

by default, a JPanel has flow layout and a JFrame's content pane has border layout

layouts can be nested in the sense that a JPanel with flow layout can be placed in one of the regions of the content pane of a JFrame to allow multiple components to be placed into a JFrame's content pane (see Figure 11.9 on page 320)

BlueJ: projects | chapter06 | calculator-gui
chapter06/calculator-gui/CalcEngine.java
chapter06/calculator-gui/Calculator.java
chapter06/calculator-gui/README.TXT
chapter06/calculator-gui/UserInterface.java

here is what the image viewer does: labels in the north and south regions with the image in the center region

...
Container contentPane = frame.getContentPane();

// Specify the layout manager with nice spacing
contentPane.setLayout(new BorderLayout(6, 6));

filenameLabel = new JLabel();
contentPane.add(filenameLabel, BorderLayout.NORTH);

imagePanel = new ImagePanel();
contentPane.add(imagePanel, BorderLayout.CENTER);

statusLabel = new JLabel(VERSION);
contentPane.add(statusLabel, BorderLayout.SOUTH);
...

add image filters: lighter, darker, threshold

add help menu and ``about'' message dialog box

BlueJ: projects | chapter11 | imageviewer1-0
chapter11/imageviewer1-0/ImageFileManager.java
chapter11/imageviewer1-0/ImagePanel.java
chapter11/imageviewer1-0/ImageViewer.java
chapter11/imageviewer1-0/OFImage.java
chapter11/imageviewer1-0/README.TXT

refactor code to handle an arbitrary number of filters without code duplication

all those methods in class ImageViewer named makeDarker, makeLighter, threshold, and others added for grayscale, mirror, invert, smooth, solarize, edge detect, fish eye, etc.

and the corresponding similarly named methods in class OFImage

abstract class Filter has an abstract method apply

class ImageViewer has a list of filter objects to which the filters are added; this is the only place that the filter names appear!

List filterList = new ArrayList();
filterList.add(new DarkerFilter("Darker"));
filterList.add(new LighterFilter("Lighter"));
filterList.add(new ThresholdFilter("Threshold"));
// add more filters here

no matter how many filters are added, their names are placed into the menu this way (the filter names do not appear and the action listener calls the apply method, not a lighter or darker, etc., named method)

// create the Filter menu
menu = new JMenu("Filter");
menubar.add(menu);

for (Iterator it=filters.iterator(); it.hasNext(); ) {
   final Filter filter = (Filter) it.next();
   item = new JMenuItem(filter.getName());
   item.addActionListener(
      new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            applyFilter(filter);
         }
      }
     );
     menu.add(item);
}

now it is much easier and less error-prone to add new filters

BlueJ: projects | chapter11 | imageviewer2-0
chapter11/imageviewer2-0/DarkerFilter.java
chapter11/imageviewer2-0/Filter.java
chapter11/imageviewer2-0/ImageFileManager.java
chapter11/imageviewer2-0/ImagePanel.java
chapter11/imageviewer2-0/ImageViewer.java
chapter11/imageviewer2-0/LighterFilter.java
chapter11/imageviewer2-0/OFImage.java
chapter11/imageviewer2-0/README.TXT
chapter11/imageviewer2-0/ThresholdFilter.java

buttons, borders, GridLayout, nested JPanels for larger and smaller buttons

BlueJ: projects | chapter11 | imageviewer3-0
chapter11/imageviewer3-0/DarkerFilter.java
chapter11/imageviewer3-0/Filter.java
chapter11/imageviewer3-0/FishEyeFilter.java
chapter11/imageviewer3-0/ImageFileManager.java
chapter11/imageviewer3-0/ImagePanel.java
chapter11/imageviewer3-0/ImageViewer.java
chapter11/imageviewer3-0/LighterFilter.java
chapter11/imageviewer3-0/OFImage.java
chapter11/imageviewer3-0/README.TXT
chapter11/imageviewer3-0/ThresholdFilter.java

BlueJ: projects | chapter11 | imageviewer-final
chapter11/imageviewer-final/DarkerFilter.java
chapter11/imageviewer-final/EdgeFilter.java
chapter11/imageviewer-final/Filter.java
chapter11/imageviewer-final/FishEyeFilter.java
chapter11/imageviewer-final/GrayScaleFilter.java
chapter11/imageviewer-final/ImageFileManager.java
chapter11/imageviewer-final/ImagePanel.java
chapter11/imageviewer-final/ImageViewer.java
chapter11/imageviewer-final/InvertFilter.java
chapter11/imageviewer-final/LighterFilter.java
chapter11/imageviewer-final/MirrorFilter.java
chapter11/imageviewer-final/OFImage.java
chapter11/imageviewer-final/PixelizeFilter.java
chapter11/imageviewer-final/README.TXT
chapter11/imageviewer-final/SmoothFilter.java
chapter11/imageviewer-final/SolarizeFilter.java
chapter11/imageviewer-final/ThresholdFilter.java

copy and paste the graphics idioms you need out of the sound player application

model/view separation for good cohesion: model is class SoundEngine and view is class SoundPlayerGUI

remember the foxes and rabbits AnimatedView and TextView classes that both implement the SimulatorView interface

extend JFrame instead of encapsulate a JFrame object as ImageViewer does

displaying an image as part of a GUI; the following displays an image instead of a text label

JLabel image = new JLabel(new ImageIcon("title.jpg"));

combo-box

String[] formats = { "all formats", ".wav", ".au", ".aif" };
JComboBox formatList = new JComboBox(formats);
formatList.addActionListener(this);
leftPane.add(formatList, BorderLayout.NORTH);

list, scrollbar

fileList = new JList(audioFiles);
fileList.setForeground(new Color(140,171,226));
fileList.setBackground(new Color(0,0,0));
fileList.setSelectionBackground(new Color(87,49,134));
fileList.setSelectionForeground(new Color(140,171,226));
JScrollPane scrollPane = new JScrollPane(fileList);
scrollPane.setColumnHeaderView(new JLabel("Audio files"));
leftPane.add(scrollPane, BorderLayout.CENTER);

slider

slider = new JSlider(0, 100, 0);
TitledBorder border = new TitledBorder("Seek");
border.setTitleColor(Color.white);
slider.setBorder(new CompoundBorder(new EmptyBorder(6, 10, 10, 10), border));
slider.addChangeListener(this);
slider.setBackground(Color.BLACK);
slider.setMajorTickSpacing(25);
slider.setPaintTicks(true);
centerPane.add(slider, BorderLayout.SOUTH);

BlueJ: projects | chapter11 | simplesound
chapter11/simplesound/audio/applause.au
chapter11/simplesound/audio/armstrong.wav
chapter11/simplesound/audio/bell.aif
chapter11/simplesound/audio/bluejay.au
chapter11/simplesound/audio/fanfare.wav
chapter11/simplesound/audio/Figaro.au
chapter11/simplesound/audio/guitar.wav
chapter11/simplesound/audio/hallelujah.wav
chapter11/simplesound/audio/howling.wav
chapter11/simplesound/audio/kookaburra.au
chapter11/simplesound/doc/index.html
chapter11/simplesound/README.TXT
chapter11/simplesound/SoundEngine.java
chapter11/simplesound/SoundPlayerGUI.java
chapter11/simplesound/title.jpg


home page: http://elvis.rowan.edu/~hartley/index.html
e-mail: hartley@elvis.rowan.edu