Swing - CT | UB



Advanced Swing Components

____________________________________

[pic]

| |

| |

Swing

Swing is a GUI toolkit for Java. It is one part of the Java Foundation Classes (JFC). Swing includes graphical user interface (GUI) widgets such as text boxes, buttons, split-panes, and tables.

Swing widgets provide more sophisticated GUI components than the earlier Abstract Window Toolkit. Since they are written in pure Java, they run the same on all platforms, unlike the AWT which is tied to the underlying platform's windowing system. Swing supports pluggable look and feel – not by using the native platform's facilities, but by roughly emulating them. This means you can get any supported look and feel on any platform.

The disadvantage of lightweight components is slower execution. The advantage is uniform behavior on all platforms. *

* Wikipedia

Where can I get more resources on Swing GUI?

Sun provides links for tutorials on various Swing components.



They provide files ready to run in Netbeans IDE. They can all be adapted to run on other IDEs as well in editors such as EditPlus, or even command line.



Bundles of Current Material which are updated to keep up with new Java releases



|Bundle |Compressed / |HTTP Download |

| |Uncompressed | |

|Online Tutorial |26.2 MB / 49.6 MB |tutorial.zip |

|(last updated April 30, 2007) | | |

|Tutorial Examples |1.2 MB / 2.5 MB |tut-examples.zip |

|(Not including the Swing Tutorial examples; | | |

|last updated April 30, 2007) | | |

|Swing Trail |28.3 MB / 39.6 MB |tut-swing.zip |

|(Including the Swing Tutorial Examples; | | |

|last updated April 30, 2007) | | |

|Swing Tutorial Examples |3.4 MB / 21.9 MB |SwingTutorialExamples.zip* |

|(last updated April 30, 2007) | | |

Java Programs Types

Applications

Applications run on the client machine and have all rights to the resources of the computer as any other programs in any language

Applets

Applets are resident on the server and are downloaded in bytecode to the client machine. Applets are real Java programs which run in the browser environment. They have the capability to communicate to their browser context. The applet runs in a sandbox by default. It has restrictions to the machine resources, such as the local file system, peripherals, and networking ability.

Servlets

Servlets are Java programs which execute in the Web Server (container) environment. The can take parameters and have no restrictions. They have no GUI and typically their output stream is sent to the client program (typically, but not always a browser). These are commonly used in web applications.

Midlets

Midlets are Java programs written to execute on small PDAs, such as cell phones with a JVM installed. To write a MIDlet, you can get the JWT (Java Wireless Toolkit) or an IDE such as Netbeans with the NetBeans Mobility Pack.

Java Programs Types

Web Applications

Web Applications are a client server relationship, where the client connects to the web server through the HTTP protocol. The request typically carries some information about the client request, often user name, account, or password.

The web server response is sent back to the browser with objects that the typical browser can understand, HTML, Javascript, Applets, Flash, . . .

Since the HTTP protocol is connectionless in nature a request/response sequence terminates the connection. A client must issue another request to get further information. In order not to lose information about the client’s last connection, web servers use a session ID. This is nothing more than a header field sent back from the server after the initial communication sequence. The client’s browser will sent that header along with any other information to the server. This helps the server to keep track of the status or state of the connection.

Web applications are really system applications, which are open many concurrent users. Because the client request often travel and navigates through pages over many consecutive communications, web apps have variables to keep track of the application state on the user, and the global level (for all users). Web apps can create and access objects to keep track of the application state using a variety of scopes. These include page, request, session, and application.

Java technology, which facilitates these web applications, is strongly based on Servlets and JSPs (Java Server Pages). Servlets and JSPs run entirely in the server, have no restrictions on access of resources, and can communicate to their context (the web applications). The present no GUI so what is rendered to the client is done with HTML and Javascript. These codes are often generated as output and streamed to the client machine.

GUI Choices in Distributed Applications

Client Applications

Client Applications can be Swing , AWT or SWT GUI programs. The can be installed on the client, downloaded and executed by an automated system (such as JNLP using Java Web Start), or download as Jar or Installation file manually by the user of the client machine.

Applets

Applets can have a full GUI just as Client Applications, they have the advantage of being run on thin clients, and can be modified in a central place without client machine modification. Their restriction is the sandbox and this can be overcome with modification of permissions on the client machine or through a signing mechanism.

Web Applications

Web Applications are strong in processing power, as the infrastructure is solid for backend processing. GUI is the weakest and least integrated part of this type of application. One choice is to use HTML or even DHTML (HTML, Javascript, CSS, DOM). These techniques are unsatisfying or at least not as integrated and flexible as a real GUI environment.

Solution choices include using

1. Applets as part of the presentation

2. JSF technology

3. JNLP

4. GWT

Java Swing Components

Swing Design Goal

To build a set of extensible GUI components to enable developers to more rapidly develop powerful Java front ends for commercial applications.

How it was achieved

1. Be implemented entirely in Java to promote cross-platform consistency and easier maintenance.

2. Provide a single API capable of supporting multiple look-and-feels so that developers and end-users would not be locked into a single look-and-feel.

3. Enable the power of model-driven programming without requiring it in the highest-level API.

4. Adhere to Java Beans design principles to ensure that components behave well in IDEs and builder tools.

5. Provide compatibility with AWT APIs where there is overlapping, to leverage the AWT knowledge base and ease porting.

Swing MVC – Model View Controller Architecture

[pic]

A model that represents the data for the application.

The view that is the visual representation of that data.

A controller that takes user input on the view and translates that to changes in the model.

Swing MVC – Model View Controller Architecture

The real Swing architecture is more like shown below.

[pic]

Separable Model Architecture

The V and C parts are the component and the UI object. The UI object’s parts need to be tightly integrated.

Separable Model Architecture (SMA)

Each Swing component has a separate model component for a swing component. Default models exist, but can be customized for any Swing class. Some examples are shown below.

|Component | Model Interface | Model Type |

|JButton |ButtonModel |GUI |

|JToggleButton |ButtonModel |GUI/data |

|JCheckBox |ButtonModel |GUI/data |

|JTabbedPane | SingleSelectionModel | GUI |

|JList |ListModel |data |

|JList |ListSelectionModel |GUI |

|JTable |TableModel |data |

|JTable |TableColumnModel |GUI |

|JTree |TreeModel |data |

|JTree |TreeSelectionModel |GUI |

SMA Architecture Model Independence

Swing Renderers

A renderer is a Swing helper class to help the JVM create the visual aspect of a swing component. Classes that contain collections of components allow the programmer to describe how elements in these collections should be displayed.

The content object may be a String or some other class, while the render my display it as a JLabel, JButton, JComboBox or something else.

Think what you see as a representation of what is actually there.

A Swing Renderer for a JTable (inner class)

class TimeTableRenderer extends JLabel implements TableCellRenderer {

private TableCellRenderer defaultRenderer;

private Font font = new Font("Helvetica", Font.BOLD, 10);

public TimeTableRenderer() {

setOpaque(true);   setFont(font); setHorizontalAlignment(SwingConstants.CENTER);}

public Component getTableCellRendererComponent (JTable table, Object value,

    boolean isSelected, boolean hasFocus, int row, int column) {

    if (table.getRowCount() == 0)

        return null;

        if (value.toString().equals("")) {

            setBackground(Color.black);

            Border b = BorderFactory.createEmptyBorder();

            this.setBorder(b); }

       

else {

            if (Integer.parseInt(((String)value).substring(0,2)) % 2 == 0) {

                setBackground(Color.blue);

                setForeground(Color.white); }

            else {

                setBackground(Color.yellow);

                setForeground(Color.black); }}

    if (value == null) { setText("");    setToolTipText(""); }

    else {  setText(value.toString());    setToolTipText(value.toString()); }

   

if (isSelected) {  setBackground(Color.blue);   }

repaint();

return this;  }}

Renderer in Table changed from JLabel to JButton *

[pic]

[pic]

* UI also changed from Metal to Windows

Swing Renderer and Editor Access

TableModel (interface)

Default TableModel (implementing class)

TableColumnModel (interface)

DefaultTableColumnModel (implementing class)

TableColumn (class)

Swing Custom Editor (inner class)

class MyTableCellEditor2 extends AbstractCellEditor implements TableCellEditor {

JComponent component = new JTextField();

// This method is called when a cell value is edited by the user.

// 'value' is value contained in the cell located at (row, col)

public Component getTableCellEditorComponent(JTable table, Object value,

boolean isSelected, int row, int col) {

// Configure the component with the specified value

((JTextField)component).setText((String) value);

component.setBackground(Color.orange);

component.setForeground(Color.RED);

// Return the configured component

return component; }

// Called when editing is completed.

// Returns the new value to be stored in the cell.

public Object getCellEditorValue() {

return ((JTextField) component).getText(); }}

Swing Custom Editor

[pic]

[pic]

Swing Abstract and Default Classes

Swing Components – JList

JList Constructors

JList()

JList(ListModel)

JList(Object[])

JList(Vector)

JList DefaultListModel

JList Example

Reading and listing files from a user chosen directory

[pic]

[pic]

JList Program Listing

import java.awt.*;

import javax.swing.*;

import java.io.*;

import java.awt.event.*;

public class JListDirectory extends JFrame {

private String directoryName;

public JListDirectory () {

super("File Listing w/ JList");

setSize(400,400);

setLocation(300,300);

setVisible(true);

addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) { System.exit(0); } });

JFileChooser chooser = new JFileChooser();

// Allows choosing of directories only, not ordinary files

chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

int option = chooser.showOpenDialog(this);

if (option == JFileChooser.APPROVE_OPTION) {

if (chooser.getSelectedFile()!= null){

directoryName = chooser.getSelectedFile().getAbsolutePath(); }}

File selectedDirectory = new File(directoryName);

File [] files = null;

if(selectedDirectory.isDirectory()) files = selectedDirectory.listFiles();

else {

System.err.println("Not a Directory Selected! Program exiting.");

System.exit(0); }

JList Program Listing (continued)

JList fileList = new JList(files); // JList takes Object [] as elements

fileList.setVisibleRowCount (8);

JScrollPane pane = new JScrollPane (fileList);

this.getContentPane().add(pane, BorderLayout.CENTER);

validate(); }

public static void main(String [] s) {

EventQueue.invokeLater(new Runnable() {

public void run() { new JListDirectory(); }});

}}

Try It Out

First off, run the code in the JList directory in the labs section. Make a project ion Eclipse with the JListDirectory file. Your program should look similar to the one on page 22.

Your assignment is to make a new renderer for the JList so that each file will be displayed with just the file name without the full path prefix. Also each file name should be displayed as a JLabel in alternating colors blue/yellow (see below).

[pic]

1. You will need to write a class that extends the DefaultListCellRenderer, call it MyJListRenderer.

2. You will need to override the

getListCellRendererComponent(JList, Object, int, boolean, boolean)

method. The method has to take the value (Object parameter) and use String methods to return the string following the last subdirectory “/” character. Also it will return a JLabel as a rendering with the new string as its text. Based upon the index of the element, color the JLabel yellow or blue in odd/even alternating fashion.

Swing JComboBox

The JComboBox is a list item selector, which can optionally allow for simple addition into its list of items.

The DefaultComboBoxModel has the following constructors

DefaultComboBoxModel()

DefaultComboBoxModel(Object[] items)

DefaultComboBoxModel(Vector v)

The methods for this class are

void addElement(Object anObject)

Object getElementAt(int index)

int getIndexOf(Object anObject)

Object getSelectedItem()

int getSize()

void insertElementAt(Object anObject, int index)

void removeAllElements()

void removeElement(Object anObject)

void removeElementAt(int index)

void setSelectedItem(Object anObject)

Swing JComboBox

Swing JTree

A JTree is a very flexible component used for navigating hierarchical structures. A common usage is for directories or for XML files using the hierarchical DOM model.

The Trees application is a simple example of using this component.

Swing JTree

The JTree is easily created and manipulated by the DefaultMutableTreeNode class.

Here is the code listing

This class builds a subtree from a string array. The returned tree is rooted at the node labeled by the first string, and the remaining strings are used as the labels for the children of the root. It is a non-public class contained in the same file as the public Trees class.

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.tree.*;

class Branch {

DefaultMutableTreeNode r;

public Branch(String[] data) {

r = new DefaultMutableTreeNode(data[0]);

for(int i = 1; i < data.length; i++) {

DefaultMutableTreeNode node = new DefaultMutableTreeNode(data[i]);

r.add(node); } }

public DefaultMutableTreeNode getNode() { return r; }}

Swing JTree

public class Trees extends JPanel {

String[][] data = {

{ "Trees", "Maple", "Oak", "Larch","Chestnut" },

{ "Colors", "RED", "GREEN", "BLUE","YELLOW","MAGENTA" },

{ "Names","Joseph","Julius","Fred","Sandy","Benjamin"},

{ "Fonts","Helvetica","Courier","Arila","Times","Gothic"}};

static int i = 0;

DefaultMutableTreeNode root, child, chosen;

JTree tree;

DefaultTreeModel model;

public Trees() {

setLayout(new BorderLayout());

root = new DefaultMutableTreeNode("The Root");

tree = new JTree(root);

// Use a scrollpane as a container:

add(new JScrollPane(tree), BorderLayout.CENTER);

// Capture the tree's model:

model =(DefaultTreeModel)tree.getModel();

JButton test = new JButton("Press me");

test.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e){

if(i < data.length) {

child = new Branch(data[i++]).getNode();

// find last JTree selected component

chosen = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();

if(chosen == null) chosen = root;

// The model creates the appropriate event. In response, the tree will update itself:

model.insertNodeInto(child, chosen, 0); } } });

test.setBackground(Color.blue);

test.setForeground(Color.black);

JPanel p = new JPanel();

p.add(test);

add(p, BorderLayout.SOUTH); }

Swing JTree

public static void main(String args[]) {

JFrame frame = new JFrame("JTree Example");

frame.setSize(400,400);

frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

Trees trees = new Trees();

frame.getContentPane().add(trees);

frame.setVisible(true);

}

} // Trees

Try It Out

1. Run the Trees example in the JTree/Basic Trees folder.

2. You will be able to add a new branch to the tree by clicking the button on any tree node. Run the program several times, with different tree configurations

3. Modify the contents of the data array and see how that affects the program

4. Next, modify the Branch constructor so that it makes the first string the root (as before), but each next string becomes a child of the previous node.

5. Call this variation TreesNew. Your output could/should look similar to the one below.

Swing JTree

Now run the Trees2 application in the JTree/Basic Trees folder. It runs basically the same, but there are two listeners for specific tree events. One is a TreeSelectionListener and the other is a TreeExpansionListener. They will listen for nodes being selected and expanded or contracted.

Here are code excerpts for these listeners. Here is an anonymous inner class implementer of TreeExpansionListener

Below is my class which implements the TreeSelectionListener interface

Swing JTree

Let’s run the code for JTree/DirectoryTree application. It reads the directory and creates a JTree based on that. It is not recursive so that only the root and one level of directories and files are created in the tree. A cool feature is that a file will be opened in notepad if you double click it. Here is a sample run.

[pic]

Here is the notepad code

Process proc = Runtime.getRuntime().exec("notepad " + path.getLastPathComponent());

Try It Out

1. Run and examine the code to the DirectoryTrees application in the JTree/Directory Tree folder.

2. You may want to work on this application with a partner. Create a new project with DirectoryTrees and Branch classes and give them added functionality as follows

3. First create a custom renderer implementing the TreeCellRenderer interface for the tree. It should color any leaf green and any selected node yellow. Check out the getTreeCellRendererComponent method. It carries a lot of useful information.to help you.

public Component getTreeCellRendererComponent(JTree tree,

Object value, boolean selected, boolean expanded,

boolean leaf, int row, boolean hasFocus)

4. Since the directory only expands to 3 levels initially, add more functionality so that whenever you click on a directory has not been loaded from the disk, it reads it and expands the tree by another level. In this way you can now navigate the entire directory, without having to build it completely. You build it using lazy expansion.

5. For future reference, it might be a good idea to extend the DefaultMutableTreeNode and add a Boolean property, directory. Then a method isDirectory could be interrogated by the renderer to make unopened directories stand out.

Try It Out

6. Here is a view of the solution

Swing JTable

A JTable is a grid, which is made up of cells organized in columns. You can create renderers and editors for particular columns. The JTable helper classes include DefaultTableModel and AbstractTableModel.

Swing JTable

The JTable constructors allow flexible options to make this component.

JTable()

JTable(int numRows, int numColumns)

JTable(Object[][] rowData, Object[] columnNames)

JTable(TableModel dm)

JTable(TableModel dm, TableColumnModel cm)

JTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm)

JTable(Vector rowData, Vector columnNames)

The TableModel interface has the following subclass methods in class DefaultTableModel class. Usually you subclass AbstractTableModel.

void addTableModelListener(TableModelListener l)

Class getColumnClass(int columnIndex)

int getColumnCount()

String getColumnName(int columnIndex)

int getRowCount()

Object getValueAt(int rowIndex, int columnIndex)

boolean isCellEditable(int rowIndex, int columnIndex)

void removeTableModelListener(TableModelListener l)

void setValueAt(Object aValue, int rowIndex, int columnIndex)

Swing JTable

The DefaultTableModel has the following constructors. Use this is you want more control over the JTable then the assumed (default) behavior. In that case you will need to subclass this class, and specialize you methods.

DefaultTableModel()

DefaultTableModel(int rowCount, int columnCount)

DefaultTableModel(Object[][] data, Object[] columnNames)

DefaultTableModel(Object[] columnNames, int rowCount)

DefaultTableModel(Vector columnNames, int rowCount)

DefaultTableModel(Vector data, Vector columnNames)

The AbstractTableModel is an even more powerful class with additional signaling methods about the state of the model.

Swing JTable

Here is a partial example of a AbstractTableModel extension class

import javax.swing.table.*;

import java.util.*;

public class MyTableModel extends AbstractTableModel {

Vector rows;

Vector head;

public MyTableModel(Vector rows, Vector head) {

this.rows = rows;

this.head = head; }

public Object getValueAt(int row, int col) { return rows.elementAt(row).elementAt(col);}

public void setValueAt(Object value, int row, int col) {

rows.elementAt(row).setElementAt(value, col) ; }

public int getColumnCount() {return rows.elementAt(0).size(); }

public int getRowCount() { return rows.size(); }

public String getColumnName(int col) { return head.elementAt(col); }

// user-defined method

public void addRow() {Vector newRow = new Vector();

newRow.add(""); newRow.add("");newRow.add(""); newRow.add("");

rows.add(newRow);

fireTableRowsInserted(rows.size()-1, rows.size()-1) ; }

public boolean removeRows(int [] index) {

for (int i = index.length-1; i >= 0; i--) {

rows.removeElementAt(index[i]); }

fireTableRowsDeleted(index[0],index[index.length-1]) ;

System.out.println("remove Done");

return true; }

public boolean isCellEditable(int row, int col) { return true; }}

Try It Out

1. You will create a simple GUI with a JTable.

2. Create a JFrame. Make its layout a BorderLayout.

3. Add a JPanel to the south with a FlowLayout manager

4. Now add a JPanel to the center with a BorderLayout manager

5. Create a string array with entries, { “First”, “Last”, “City” }

6. Then create a 2-D string array with contents {{“George”, “Smith”, Bridgeport”}, {“Thomas”, “Burns”, “Middletown”},{“Steve”,”Dunn”,”Cheshire”}}

7. Create a JTable with these elements. and add it to a scrollpane in the upper panel. Your app should look something like this.

8. Add an event handler so that the data array is printed in table format on a button press. Are the cells editable? Modify the data in the JTable and press the button. Did the data in the 2-D array get modified? What does you answer tell you about the default data model?

9. You can find the solution in the solution folder

Try It Out

1. How would you modify your program so that ONLY the city field can be modified?

2. Let’s do this now.

3. We will have to move the table model into a separate class, and then override the DafaultTableModel method that allows access to edit table cell contents.

4. You should NOT be able to edit the name. Did it work?

5. Now modify ALL city names, and print the data with the button press. Notice that the data is NOT really modified in the model.. This is SMA, separable model architecture at work.

6. Modify so that the model’s data is also changed. You will override another method, setValueAt. Change a City, and watch what happens. Does the city change in the view? Print the model data with the button. Did it change. You will also now have to override the getValueAt method so that the display (view) is consistent with the model.

7. Now let’s sort the data with button click. To do this we will need a listener for the mouse button, on the column header on which we wish to sort. Go to the next page

Try It Out

8. First off, you need to add a mouse listener to the header. Use the getTableHeader method from JTable.

9. Now add a mouse listener to the header you just obtained. Listen for mouse pressed events.

10. The code below will obtain the column index of the column which was clicked.

(((JTableHeader)event.getSource()).getColumnModel()).getColumnIndexAtX(event.getX())

11. Now get the model of the JTable (you know that method by now) and call a newly created method sort, sending it the column index and true/false for ascending/descending sort.

12. You COULD do the sort right in your table model class, but it is easier to call an existing sort algorithm. Use the java.util.Arrays class. Call sort, passing two objects, the data 2-D array, and a Comparator implementer object.

13. Implement the Comparator interface, call it MyComparator as follows. The equals method can just return true (we do not need it at this time) and the compare method will receive two string arrays to compare. We need to reference the actual strings in each array and compare them for ordering. Since the String class has a compareTo method defined, our code will look like

return ((String[])o1)[col].compareTo(((String[])o2)[col]);

where col is the column you’re sorting on (same as the index of a string in the array)

14. The compareTo method returns an integer (0, ned or pos). You can create a second class called MyComparator2, same as the first, but return negative 1 times the above return code. This will allow a descending sort.

15. Now you should be able to sort the data in the table in ascending or descending order, and modify the 3rd column. We will create a way to add data to the model and view in a later lab.

Swing JFileChooser

A JFileChooser is a component that allows interfacing into the local file system. We can use it to show open or save file chooser windows. The actual opening or saving of a file is done using programming. The chooser simply supplies the objects for our processes.

The following example shows how we can use a chooser and a filter for viewing certain file types or even directories. It also shows how we can respond to the user’s ok or cancel responses.

Swing JFileChooser

dirButton.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent ae) {

JFileChooser chooser = new JFileChooser();

chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

int option = chooser.showOpenDialog(parent);

if (option == JFileChooser.APPROVE_OPTION) {

statusbar.setText("You opened " +

((chooser.getSelectedFile() != null)?

chooser.getSelectedFile().getName(): "nothing")); }

else {

statusbar.setText("You canceled."); } }});

openButton.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent ae) {

JFileChooser chooser = new JFileChooser();

chooser.setFileFilter(filter);

int option = chooser.showOpenDialog(parent);

if (option == JFileChooser.APPROVE_OPTION) {

statusbar.setText("You chose " +

((chooser.getSelectedFile()!=null)?

chooser.getSelectedFile().getName(): "nothing")); }

else {

statusbar.setText("You canceled."); } }});

Swing JFileChooser Filter

Here is a simple filter to allow directories through as well as files which end with a string, fileEnd;

import java.io.*;

import javax.swing.filechooser.FileFilter;

public class MyDocumentFilter extends FileFilter {

private String fileEnd;

public MyDocumentFilter (String fileEnd) { this.fileEnd = fileEnd; }

public void setEnd(String end) { fileEnd = end; }

public boolean accept(File f) {

return String.valueOf(f).endsWith(fileEnd) || f.isDirectory(); }

public String getDescription() { return "*." + fileEnd ; }}

Try It Out

1. Run the application SimpleFileChooser in the JFileChooser directory. One you are familiar with it, modify it as follows.

2. Create a new property tArea, a JteaxtArea with dimensions, (12,20). Make the layout manager of the bPanel a BoxLayout organized along the y-axis. Add the text area (encased in a scroll pane object) second so that it is below the text field. It should look like this:

3. Now we want to add real file reading and writing capability. To make it simple, we will use the Properties class. It has two methods, load and store which read a *.properties file. A properties file is a text file delimited with an equal sign (=). The property is to the left of the sign, the value to the right.

4. Let’s create a simple file like shown below, save it as info.properties.

Try It Out

5. You will need a Properties object in you application. After you set the filter for properties type files, you will select the open button.

6. I have modified my open button code as follows

openButton.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent ae)

JFileChooser chooser = new JFileChooser("c:\\");

chooser.setFileFilter(filter);

int option = chooser.showOpenDialog(parent);

if (option == JFileChooser.APPROVE_OPTION) {

statusbar.setText("You chose " + ((chooser.getSelectedFile()!=null)?

chooser.getSelectedFile().getName(): "nothing"));

String propFile = chooser.getSelectedFile().getName();

if (propFile.endsWith(".properties"))

displayPropertiesFile(propFile); }

else {

statusbar.setText("You canceled."); } } });

7. You will need to code the displayPropertiesFile method. It will receive the properties file name. In your method the code props.load(new FileInputStream(fileName)) will load from the file all the properties into the props field. Properties has a method propertyNames, which returns an Enumeration to the property keys. You can retrieve properties from the props object with getProperty(keyValue). Both the property and the keys are String objects to make like simple. Once you have the data, you can use the setText method from JTextArea to display the file. The result is shown on the next page. The code below is a sample for the Properties class.

Enumeration e = props.propertyNames();

String key = e.nextElement()

String value = props.getProperty(key);

Try It Out

JPopupMenu

This Swing component is just a simple variation of the JMenu. It is used in conjunction with the same subcomponents (JMenu, JMenuItem) and with the ActionListener for menu actions. To trigger the JPopupMenu you typically need to have a MouseListener.

JPopupMenu

The code of a typical MouseListener

Try It Out

1. Run the code in Eclipse for the JPopupMenu/Simple JPopupMenu application.

2. Now add another JPopupMenu which looks like this.

3. Make the new JPopupMenu trigger off the left click of the mouse.

4. Both JPoPupMenus should be active

Drag and Drop

The ability to do drag and drop actions is enabled in simple cases by enabling the drag property to true.

Some classes, like TextComponent, JColorChooser, which have a well, defined element to drag out or in make the task simple.

Java can make the ability to drag and drop easy by utilizing the TransferHandler class. In this case you can use named properties. The element being dragged out/in is packaged into a Transferable interface implementer.

[pic]

Drag and Drop

You can set the drag support ON by using setDragEnabled(true) on some Components.

Here the support is OFF, and highlighting works as normal.

[pic]

Now we can turn it ON or OFF using the corresponding JCheckBox

[pic]

Dragging from the left to the right JTextFields

[pic]

Go to Lab 5 in the lab section of this book. (Drag1)

Drag and Drop

The Transferable interface can be used to pass properties between objects. For example the JTextField can import and export a text property. A JLabel has a many properties, one of which is text. The standard methods are setText() and getText().

We can create a TransferHandler to set these JLabel properties with the following code.

This allows receiving of a property called text from another component. Compatibility is done through a DataFlavor mechanism. There are three DataFlavor (string, image and file), and users can also create their own.

[pic]

[pic]

Drag and Drop

To enable dragging the text field out of the label, we need to pick up the MouseEvent on the label, and react appropriately. The following code illustrates that:

Below is a private inner class to make the export out of the label possible:

Go to Lab 5a in the lab section of this book. (Drag1)

Drag and Drop and the DataFlavor Class

The JFileChooser has a built-in support for DnD. You can enable it with the setDragEnabled() method.

Run the Dnd/ColorFlavor/DragColorDemo program in Eclipse. You should be able to drag a selected color into ant of the buttons. The check box will toggle between changing the foreground and background colors.

Before After

Drag and Drop and the DataFlavor Class

In this example the buttons have a custom TransferHandler that allows imports of dragged Color.

The import() method is shown below. The incoming TransferHandler gets the Transferable object from the outgoing TransferHandler, and fetches the data from it.

Interestingly the Transferable can feature various Flavors although the importer is looking for a particular one.

Full ColorTransferHandler Code

The Full Handler Code

public class ColorTransferHandler extends TransferHandler {

//The data type exported from JColorChooser.

String mimeType = DataFlavor.javaJVMLocalObjectMimeType + ";class=java.awt.Color";

DataFlavor colorFlavor;

private boolean changesForegroundColor = true;

ColorTransferHandler() {

//Try to create a DataFlavor for color.

try { colorFlavor = new DataFlavor(mimeType);}

catch (ClassNotFoundException e) { } }

public boolean importData (JComponent c, Transferable t) {

if (hasColorFlavor(t.getTransferDataFlavors())) {

try {

Color col = (Color)t.getTransferData(colorFlavor);

if (getChangesForegroundColor()) { c.setForeground(col);}

else { c.setBackground(col); }

return true;}

catch (UnsupportedFlavorException ufe) {

System.out.println("importData: unsupported data flavor"); }

catch (IOException ioe) {System.out.println("importData: I/O exception"); }}

return false; }

protected boolean hasColorFlavor (DataFlavor[] flavors) {

if (colorFlavor == null) { return false; }

for (int i = 0; i < flavors.length; i++) {

if (colorFlavor.equals(flavors[i])) { return true; } }

return false; }

public boolean canImport (JComponent c, DataFlavor[] flavors) {

return hasColorFlavor(flavors); }

protected void setChangesForegroundColor (boolean flag) {

changesForegroundColor = flag; }

protected boolean getChangesForegroundColor() {

return changesForegroundColor; }}

Try It Out

1. Run the code in Eclipse for the Dnd/ColorFlavor application.

2. Try changing various buttons’ foreground and background colors

3. Does the color drag operation work back to the JColorChooser?

It does NOT. Think about it. Why not?

Support for Import/Export of User Data Flavors

The full TransferHandler class interface

boolean canImport(JComponent comp, DataFlavor[] transferFlavors)

protected Transferable createTransferable(JComponent c)

void exportAsDrag(JComponent comp, InputEvent e, int action)

protected void exportDone(JComponent source, Transferable data, int action)

void exportToClipboard(JComponent comp, Clipboard clip, int action)

static Action getCopyAction()

static Action getCutAction()

static Action getPasteAction()

int getSourceActions(JComponent c)

Icon getVisualRepresentation(Transferable t)

boolean importData(JComponent comp, Transferable t)

DnD Export and Import of Unsupported Flavors

The Handler Code

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

import java.awt.datatransfer.*;

import java.awt.dnd.*;

import java.io.*;

public class MyJLabelTransferHandler extends TransferHandler {

String mimeType = DataFlavor.javaJVMLocalObjectMimeType +

";class=javax.swing.JLabel";

DataFlavor labelFlavor;

MyJLabelTransferHandler(String s) {

super(s);

//Try to create a DataFlavor for JLabel

try { labelFlavor = new DataFlavor(mimeType); }

catch (ClassNotFoundException e) { } }

public boolean importData(JComponent c, Transferable t) {

JLabel label = new JLabel("VOID" );

if (hasLabelFlavor(t.getTransferDataFlavors())) {

try {

label = (JLabel)t.getTransferData(labelFlavor);

} catch (UnsupportedFlavorException ufe) {

System.out.println("importData: unsupported data flavor");

return false;

} catch (IOException ioe) {

System.out.println("importData: I/O exception");

return false;

}

}

c.add(label);

c.validate();

return true; }

The Handler Code

// This class has to prepare the Transferable for the export

// This is so you can return the Transferable object of this component

public Transferable createTransferable (final JComponent c) {

return new Transferable() {

public Object getTransferData (DataFlavor flavor) {

return ((MyPanel)c).getLabel(); }

public DataFlavor[] getTransferDataFlavors() {

DataFlavor[] dfa = new DataFlavor[1];

dfa[0] = labelFlavor;

return dfa; }

public boolean isDataFlavorSupported (DataFlavor flavor) { return true; }};

}

protected void exportDone (JComponent source, Transferable t, int action) {

JLabel label = null;

try { label = (JLabel)t.getTransferData(labelFlavor); }

catch (UnsupportedFlavorException ufe) {

System.out.println("importData: unsupported data flavor"); }

catch (IOException ioe) {

System.out.println("importData: I/O exception"); }

((MyPanel)source).remove(label);

((MyPanel)source).repaint(); }

protected boolean hasLabelFlavor (DataFlavor[] flavors) {

if (labelFlavor == null) { return false; }

for (int i = 0; i < flavors.length ; i++) {

if (labelFlavor.equals(flavors[i])) {return true; } }

return false; }

public boolean canImport (JComponent c, DataFlavor[] flavors) {

return hasLabelFlavor(flavors); }}

The Driver Code

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

import java.awt.datatransfer.*;

public class JLabelTransferTest extends JFrame {

JLabel l1, l2;

MyPanel p1, p2; // user-defined class

public JLabelTransferTest() {

super("JLabel Transfer Test");

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

setBounds(100,150,300,400);

initialize();

setVisible(true); }

public void initialize() {

JPanel cp = new JPanel();

this.setContentPane(cp);

cp.setLayout(new BorderLayout());

l1 = new JLabel("I am a Label");

l1.setOpaque(true);

l1.setBackground(Color.magenta);

l2 = new JLabel("I am also a Label");

l2.setOpaque(true);

l2.setBackground(Color.yellow);

p1 = new MyPanel(new FlowLayout()); p1.setBackground(Color.WHITE); p1.add(l1);

p2 = new MyPanel(new FlowLayout()); p2.setBackground(Color.GREEN); p2.add(l2);

cp.add(p1, BorderLayout.NORTH);

cp.add(p2, BorderLayout.SOUTH);

p1.setTransferHandler(new MyJLabelTransferHandler("label"));

p1.addMouseListener(new MyMouseListener());

p2.setTransferHandler(new MyJLabelTransferHandler("label")); }

public static void main(String [] s) {

new JLabelTransferTest(); }}

The Driver Code

class MyMouseListener extends MouseAdapter {

public void mousePressed(MouseEvent e) {

System.out.println("Pressed...");

MyPanel p = (MyPanel)e.getSource();

TransferHandler handler =p.getTransferHandler();

handler.exportAsDrag(p, e, TransferHandler.COPY_OR_MOVE); }

} // non Public MyMouseListener class

MyPanel Class

import javax.swing.*;

import java.awt.*;

public class MyPanel extends JPanel {

JLabel label;

public MyPanel (LayoutManager lm) {

super(lm); }

public Component add (Component c) {

System.out.println("adding");

label = (JLabel) c;

return super.add(c); }

public JLabel getLabel() {

return label; }

public void addLabel(JLabel label) {

add(label); }}

Try It Out

1. Run the code in Eclipse for the Dnd/Label Transfer application.

2. Notice that the yellow button cannot be dragged out. Why not?

3. Solve the problem so that you can drag either button out.

JScrollPane

The JScrollPane is a JComponent which is used to house other components which may become larger than the allotted space in the container. The JScrollPane creates scrollbars, vertically and horizontally to accommodate the viewing navigation.

JScrollPane has the following constructors

JScrollPane()

JScrollPane(Component view)

JScrollPane(Component view, int vsbPolicy, int hsbPolicy)

JScrollPane(int vsbPolicy, int hsbPolicy)

Where the scrollbar policy can be one of the following constants

JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED

JScrollPane.VERTICAL_SCROLLBAR_NEVER

JScrollPane.VERTICAL_SCROLLBAR_ALWAYS

JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED

JScrollPane.HORIZONTAL_SCROLLBAR_NEVER

JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS

Try It Out

1. Run the code in Eclipse for the JScrollPane/JListDirectoryScroll application.

2. Force the scrollbars to be visible whether or not the inside object requires it.

GUI Building

In Java GUI building is dependent on Containers and Components. Components are objects which have a visual rendering.

Containers are able to have children (components) and to display them visually. The hierarchy of classes looks like this.

java.lang.Object

[pic]java.ponent

[pic]java.awt.Container

[pic]javax.swing.JComponent

All classes here are direct subclasses of JComponent

AbstractButton BasicInternalFrameTitlePane Box Box.Filler JColorChooser JComboBox JFileChooser JInternalFrame JInternalFrame.JDesktopIcon JLabel JLayeredPane JList JMenuBar JOptionPane JPanel JPopupMenu JProgressBar JRootPane JScrollBar JScrollPane JSeparator JSlider JSpinner JSplitPane JTabbedPane JTable JTableHeader JTextComponent JToolBar JToolTip JTree Jviewport

GUI Building

The basic container of GUI building is the JPanel. In applications it is used as the contentPane of the JFrame. The entire GUI will be held in the content pane.

The JPanel is a container, but it is also a component. So it can be used to play both roles. How many panels do you see?

GUI Building

There are 8 panels shown.

GUI Building

Any JPanel always has a LayoutManager which dictates where each of its components will be drawn, and how much space it will be allotted. JComponent has a preferred size attribute, and it can be set as well as checked with the get methods. The Layout Managers call these methods as necessary to display the GUI as it is built, as it changes, and as it is resized.

Components provide methods like setMinimumSize, setPreferredSize, and setMaximumSize to assist the LayoutManager.

Methods such as validate, invalidate, and repaint are ways to update the visual rendering of a component.

What is a Layout Manager? It is an interface. We could define our own type.

java.lawt.LayoutManger

[pic]java.awt.LayoutManager2

[pic]java.awt.BorderLayout

Below are other LayoutMangers

BoxLayout, CardLayout, FlowLayout, GridBagLayout, GridLayout

GUI Building - Layout Managers

There are Seven basic Layout Managers and null, no LayoutManager

1. FlowLayout - components are added from left to right in the order that they are added to the applet

2. BorderLayout - components are added to any of five (5) locations relative to the applet, east, west, north, south, and center. Order of adding components is not important

3. GridLayout - components are added to a row-column position. All components are the same size, and they are added in row major order, thus order of adding components is important.

4. BoxLayout – components are laid out vertically of horizontally but are not wrapped if the container changes size. In a vertical layout, one component will be shown per row.

5. null – not location or size will be inferred, all objects must use the setSize, setLocation, or setBounds methods to be visible.

6. CardLayout – the layout has a master panel and a set of panels hidden within it. The hidden panels are added and registered with name, and can be displayed in arbitrary order.

7. GridBagLayout – Similar to the GridLayout, in this one each component can occupy one or more cells horizontally and vertically defining a space. The component can be positioned in different ways within that space.

The null Layout Manager

This is the way most traditional GUI builders work. You have a container, and each component can be add as long as the programmer provides the location (x,y) and the dimension (width, height) of each component placed on the GUI.

The methods are from the Component class.

setLocation (x, y)

setSize (width, height);

setBounds (x, y, width, height);

If the GUI is resized components do not move or adjust. Some of the GUI may be lost if the container becomes too small for the specified sizes.

JTabbedPane

This is a container which is used to show one of any number of other containers. A simple example can be built with an IDE or without. See below.

JTabbedPane

Here is a simple JTabbedPane application which can be easily manually coded.

Try It Out

Here is the code for the previous JTabbedPane example. I am including only the non-obvious parts to save space. Generate this GUI manually following the code provided below.

The code below is cool because it will launch you application centered on the screen

Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();

setLocation( (screenDim.width - getSize().width) / 2,

(screenDim.height -getSize().height) / 2 );

tPane.setTabPlacement();

tPane.setName("Basic");

tPane.addTab("Basic", null, panel1, “watch it!”);

tPane.setName("Advanced");

tPane.addTab("Advanced", null, panel2, “buy low sell high”);\

The full-blown addTab method

addTab(String title, Icon icon, Component component, String tip)

FlowLayout Example

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

public class FlowLayoutDemo extends JApplet implements ActionListener {

private JButton left, center, right;

public void init() {

setLayout( new FlowLayout());

left = new JButton( "Left" );

left.addActionListener( this );

add( left );

center = new JButton( "Center" );

center.addActionListener( this );

add( center );

right = new JButton( "Right" );

right.addActionListener( this );

add( right ); }

public void actionPerformed( ActionEvent e ) {

int align;

if ( e.getSource() == left )

align = FlowLayout.LEFT;

else if ( e.getSource() == center )

align = FlowLayout.CENTER;

else

align = FlowLayout.RIGHT;

setLayout( new FlowLayout( align ) );

validate();

} }

BorderLayout Example

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

public class Border extends JApplet implements ActionListener {

private JButton b[];

private String names[] = { "Hide UP", "Hide DOWN", "Hide RIGHT",

"Hide LEFT", "Hide MIDDLE" };

public void init() {

setLayout( new BorderLayout( 5, 5 ) );

// create the button reference array

b = new JButton[ names.length ];

for ( int i = 0; i < names.length; i++ ) {

b[ i ] = new JButton( names[ i ] );

b[ i ].addActionListener( this );

}

add( b[ 0 ], BorderLayout.NORTH );

add( b[ 1 ], BorderLayout.SOUTH );

add( b[ 2 ], BorderLayout.EAST );

add( b[ 3 ], BorderLayout.WEST );

add( b[ 4 ], BorderLayout.CENTER ); }

public void actionPerformed( ActionEvent e ) {

for ( int i = 0; i < b.length; i++ )

if ( e.getSource() == b[ i ] )

b[ i ].setVisible( false );

else

b[ i ].setVisible( true );

validate(); } }

GridLayout Example

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

public class Grid extends JApplet implements ActionListener {

private JButton b[];

private String names[] =

{ "one", "two", "three", "four", "five", "six" };

private boolean toggle = true;

public void init() {

// set layout to grid layout

setLayout( new GridLayout( 2, 3, 5, 5 ) );

b = new JButton[ names.length ];

for (int i = 0; i < names.length; i++ ) {

b[ i ] = new JButton( names[ i ] );

b[ i ].addActionListener( this );

add( b[ i ] ); } }

public void actionPerformed( ActionEvent e ) {

if ( toggle )

setLayout( new GridLayout( 3, 2 ) );

else

setLayout( new GridLayout( 2, 3, 5, 5 ) );

toggle = !toggle;

validate(); } }

CardLayout Example

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

public class CardDeck extends JApplet implements ActionListener {

private JPanel c;

private CardLayout cardManager;

private JPanel deck, p1, p2, p3, p4;

private JButton b1, b2, b3, prevButton, nextButton, lastButton, firstButton;

public void init() { setLayout( new BorderLayout() ); // applet

b1 = new JButton( "card one " );

b2 = new JButton( "card two " );

b3 = new JButton( "card three" );

c = new JPanel();

c.setBackground( Color.green );

c.setSize( 80, 80 );

deck = new JPanel();

cardManager = new CardLayout(); // instantiate object

deck.setLayout( cardManager ); // set cardLayout

add( "East", deck );

p1 = new JPanel(); // set up card #1

p1.add( b1 ); // add a button

deck.add( b1.getLabel(), p1 ); // add card to deck

p2 = new JPanel(); // set up card #2

p2.add( b2 ); // add a button

p2.add( c ); // add a JPanel

deck.add( b2.getLabel(), p2 ); // add card to deck

p3 = new JPanel(); // set up card #3

p3.setLayout( new BorderLayout() ); // set layout

p3.add( "North", new JButton( "North Pole" ) );

p3.add( "West", new JButton( "The West" ) );

p3.add( "East", new JButton( "Far East" ) );

p3.add( "South", new JButton( "South Pole" ) );

p3.add( "Center", b3 );

deck.add( b3.getLabel(), p3 ); // add card to deck

// create and layout panel that will control deck

p4 = new JPanel();

p4.setLayout ( new GridLayout( 2, 2 ) );

p4.add( firstButton = new JButton( "First" ) );

p4.add( nextButton = new JButton( "Next" ) );

p4.add( prevButton = new JButton( "Previous" ) );

p4.add( lastButton = new JButton( "Last" ) );

add( "West", p4 );

prevButton.addActionListener(this);nextButton.addActionListener(this);

lastButton.addActionListener(this);firstButton.addActionListener(this);}

CardLayout Example

public void actionPerformed( ActionEvent e ) {

Object obj = e.getSource();

if ( obj == firstButton )

cardManager.first( deck );

else if ( obj == prevButton )

cardManager.previous( deck );

else if ( obj == nextButton )

cardManager.next( deck );

else if ( obj == lastButton )

cardManager.last( deck );

else cardManager.show(deck, “card three”); }}

BoxLayout

BoxLayout is a way to build components in a horizontal or vertical plane. Classes Box and Box.Filler provide invisible components to layout components with more control.

|Type |Size Constraints |How to Create |

|rigid area |[pic] |Box.createRigidArea(size) |

|glue |horizontal |[pic] |Box.createHorizontalGlue() |

| |vertical |[pic] |Box.createVerticalGlue() |

|custom Box.Filler |(as specified) |new Box.Filler(minSize, prefSize, |

| | |maxSize) |

BoxLayout Example

package layout;

import javax.swing.JFrame;

public class MyLayout extends JFrame {

private javax.swing.JPanel jContentPane = null;

private javax.swing.JButton jButton = null;

private javax.swing.JScrollBar jScrollBar = null;

private javax.swing.JLabel jLabel = null;

public static void main(String[] args) {

new MyLayout();

}

public MyLayout() {

super();

initialize();

show();

}

private void initialize() {

this.setSize(200, 150);

this.setContentPane(getJContentPane());

this.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);}

private javax.swing.JPanel getJContentPane() {

if (jContentPane == null) {

jContentPane = new javax.swing.JPanel();

jContentPane.setLayout(new javax.swing.BoxLayout(jContentPane,

javax.swing.BoxLayout.Y_AXIS));

jContentPane.add(getJButton(), null);

jContentPane.add(getJScrollBar(), null);

jContentPane.add(getJLabel(), null); }

return jContentPane; }

private javax.swing.JButton getJButton() {

if(jButton == null) {

jButton = new javax.swing.JButton();

jButton.setText("Ok"); }

return jButton; }

Using BoxLayout

private javax.swing.JScrollBar getJScrollBar() {

if(jScrollBar == null) {

jScrollBar = new javax.swing.JScrollBar();

jScrollBar.setOrientation(javax.swing.JScrollBar.HORIZONTAL); }

return jScrollBar; }

private javax.swing.JLabel getJLabel() {

if(jLabel == null) {

jLabel = new javax.swing.JLabel();

jLabel.setText("I am a JLabel"); }

return jLabel; }

} // MyLayout

Using the X Axis

Using the Y Axis

Try It Out

Take the time now to run the Layout Manager examples located in the directories listed below. Make changes to the applications and applets to see how they influence the GUI.

Labs/FlowLayoutDemo

Labs/GridLayoutDemo

Labs/BorderLayoutDemo

Labs/BoxLayoutDemo

Labs/CardLayoutDemo

Try It Out

Run the applet in the Layout1 folder.

It should look like this.

Create a new component called Calculator, and then add it to the CENTER of the BorderLayout of the applet. It should look like this

GridBagLayout

You are going to use the Eclipse IDE to write a GridBagLayout like the one shown.

[pic]

Here is a close up of the display

[pic]

GridBagLayout

Once you have the basic layout, you will need to add GridBabConstraints to make the components fill out the empty areas. Right click on any component and you will be able to customize the layout of the selected component.

Here are some of the GridBagConstraints properties. These are all public so you can change them directly on a GridBagConstraints object.

The key fields that can be set in a

int anchor

used when the component is smaller than its display area.

int fill

when the component's display is larger than its requested size.

int gridheight

number of cells in a column for the component's display area.

int gridwidth

number of cells in a row for the component's display area.

int gridx

the cell at the left of the component's display area, where the leftmost cell has gridx=0.

int gridy

the cell at the top of the component's display area, where the topmost cell has gridy=0.

Insets insets

specifies the external padding of the component, the minimum amount of space to the edges of its display area.

int ipadx

how much space to add to the minimum width of the component.

int ipady

how much space to add to the minimum height of the component.

double weightx

Specifies how to distribute extra horizontal space.

double weighty

Specifies how to distribute extra vertical space.

Event Handler Thread Pattern

Normally an event handler runs its code in the same thread as the event generator. For example if an application has a few buttons with listeners, then pressing a button will trigger the active thread to run over the handler code. If this event runs for a long period of time, the applications will be unresponsive to other incoming events.

Hitting the stop button or even closing the window will not be responded to by the applet or application.

Try It Out

1. Run the code under Layout1 in Eclipse. You can run it as a Java bean.

2. Press the start button. You will see the numbers incrementing in the text field. In this case it is just busy work as you can see from the code below.

3. Try to close the application as soon as you can. Does it stop? No.

4. Every Java application or applet is a single-thread model.

Here is the main event handling code.

5. The bottom handler tries to stop the first handler by setting the class property i to 100, a terminal value. But it never gets a chance until the start handler is done.

Event Handler Thread Pattern

We can solve the problem by

1. Writing the substitute handler code in a separate method in the application, to be used as a callback method.

2. The actual handler, in this case the actionPerformed method, has access to the application reference and the name of the substitute handler method.

3. By using reflection, we can send that information to a new thread, so that its run method makes a callback to the application.

4. Here is the new handler code pattern

5. The complete event handler thread is below.

Try It Out

1. Run the code under EventGUI2 in Eclipse. You can run it as a Java bean.

2. You should be able to use the Stop button to quickly stop the application from running, and restart it using the start button. Also the close window can be used to close the application in the middle of the event handler

3. Using the GridBagLayout, place a JToggleButton in location (1,2) and a JLabel at location (1,0). Let the JButton span two rows (height = 2) and fill its space in both directions (horizontally and vertically).

4. Add an event handler itemStateChanged. That method should look similar to the actionPerformed methods. The substitute handler (callback) method can be called colorToggleAction. Code that method (with the code below) and create and start an EventRunnerThread to complete the task.

jLabel.setBackground(new Color( (float)Math.random(),

(float)Math.random(),

(float)Math.random()));

5. Your application should look similar to this. You should be able to change colors as the counter is counting as well as close the application anytime.

Observer Pattern

The observer pattern is a way for an object to watch another. The objects are referred to as the observer and the subject. This is not espionage, as the subject is a willing participant in the action.

The subject allows the observer(s) to subscripbe. The observer(s) provides callback methods that the subject can invoke.

Two interfaces make the pattern work:

Observer Pattern – The Subject

import java.util.ArrayList;

import java.util.Iterator;

public class IntegerDataBag implements Subject {

private ArrayList list = new ArrayList();

private ArrayList observers = new ArrayList();

public void add( Integer i ) {

list.add( i );

notifyObservers();

}

public Iterator iterator() {

return list.iterator();

}

public Integer remove( int index ) {

if( index < list.size() ) {

Integer i = (Integer) list.remove( index );

notifyObservers();

return i;

}

return null;

}

public void addObserver( Observer o ) {

observers.add( o );

}

public void removeObserver( Observer o ) {

observers.remove( o );

}

private void notifyObservers() {

// loop through and notify each observer

Iterator i = observers.iterator();

while( i.hasNext() ) {

Observer o = ( Observer ) i.next();

o.update( this );

}

}

}

Observer Pattern – The Observer

import java.util.Iterator;

public class IntegerAdder implements Observer {

private IntegerDataBag bag;

public IntegerAdder( IntegerDataBag bag ) {

this.bag = bag;

bag.addObserver( this );

}

public void update( Subject o ) {

if( o == bag ) {

System.out.println( "The contents of the IntegerDataBag have changed." );

int counter = 0;

Iterator i = bag.iterator();

while( i.hasNext() ) {

Integer integer = ( Integer ) i.next();

counter+=integer.intValue();

}

System.out.println( "The new sum of the integers is: " + counter );

}

}

}

Observer Pattern – Implementation

public class ObserverMain {

public static void main( String [] args ) {

Integer i1 = new Integer( 1 ); Integer i2 = new Integer( 2 );

Integer i3 = new Integer( 3 ); Integer i4 = new Integer( 4 );

Integer i5 = new Integer( 5 ); Integer i6 = new Integer( 6 );

Integer i7 = new Integer( 7 ); Integer i8 = new Integer( 8 );

Integer i9 = new Integer( 9 );

IntegerDataBag bag = new IntegerDataBag();

bag.add( i1 ); bag.add( i2 ); bag.add( i3 ); bag.add( i4 );

bag.add( i5 ); bag.add( i6 ); bag.add( i7 ); bag.add( i8 );

IntegerAdder adder = new IntegerAdder( bag );

System.out.println( "About to add an integer to the bag:" );

bag.add( i9 );

System.out.println( "About to add another integer to the bag:" );

bag.add( new Integer(100) );

System.out.println("");

System.out.println("About to remove an integer from the bag:");

bag.remove( 0 );

}

}

Try It Out

1. Run the Observer application as a project in Eclipse.

2. Modify the code and add another observer. Call the file MaxObserver. The observer will find the maximum of the bag elements and report the current maximum element in the bag.

3. You have to make a small change in the ObserverMain file to add the new observer.

Singleton Pattern

The singleton pattern is a way to control the number of instances of an object to one. To do this you need to hide the constructor from client classes.

The code below shows how this is done. The constructor is protected so clients cannot use the new operator to instantiate an object. Subclasses still can, so we could make the constructor private. This will also implicitly make the class final (non extensible).

public class SingletonClass {

private static SingletonClass instance = null;

protected SingletonClass() {

// Exists only to defeat instantiation.

}

// We construct the object (if necessary and return a reference to it

public static SingletonClass getInstance() {

if(instance == null) {

instance = new SingletonClass ();

}

return instance;

}

}

Singleton Pattern – Thread-safe Solution

A problem can occur with multithreading. Potentially two copies of the singleton class may be created if the first thread is suspended just after the getInstance method checks for singleton == null, and another thread enters the same method. Both will their own singletons (different objects)!

The solution is to modify the getInstance method to be

public synchronized static SingletonClass getInstance() {

if(instance == null) {

instance = new SingletonClass ();

}

return instance;

}

Recall that the synchronized keyword guarantees mutual exclusion.

Singleton Pattern – Serialized Solution

A problem can occur when a singleton object has been serialized, then deserialized twice. In this case two singleton objects would exist.

Serilaization is accomplished by implementation of the Serializable interface. While the interface has no required methods, there are four methods which aid in proper serializing and deserializing.

Classes requiring special handling must implement these exact methods

private void writeObject(java.io.ObjectOutputStream out) throws IOException

private void readObject(java.io.ObjectInputStream in) throws IOException,

ClassNotFoundException;

Methods that allow for substitution of objects during serialization and deserialization respectively are

ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

Singleton Pattern – Serialized Solution

public class SingletonClass implements java.io.Serializable {

public synchronized static SingletonClass getInstance() {

if(instance == null) {

instance = new SingletonClass();

}

return instance;

}

protected SingletonClass() {

// Exists only to thwart instantiation.

}

private Object readResolve() {

return getInstance();

}

}

Factory Pattern

The goal of the Factory Pattern is to hide direct object creation so the design of the application is not dependent on concrete classes and allows the client code to be constant as classes undergo modifications and new classes are added.

In its simplest case, the Factory Pattern is simply an indirect access to the constructors. The key is that the factory builds the buttons and is free to supply any subclass or any decorations on the product. The methods are usually public and static.

import javax.swing.*;

import java.awt.*;

public class SimpleButtonFactory {

public static JButton createButton(String type) {

JButton b = null;

if (type.equals("blue")) {

b = new JButton();

b.setBackground(Color.blue); }

else if ( ! (type == null)){

b = new JButton(); }

return b; }

}

Factory Pattern

A higher level of the Factory Pattern is using an interface to describe the product. Let’s say the product is a debugger. Its operations can be defined in the following interface

public interface Debugger {

public void setDebug( boolean debug );

public void debug( String message );

public void error( String message ); }

Then two or more classes can implement the interface in their own way

public class FileDebugger implements Debugger {

private java.io.PrintWriter pw;

private boolean debug;

public FileDebugger() throws java.io.IOException {

pw = new java.io.PrintWriter( new java.io.FileWriter( "c:\debug.log" ) );

}

public void setDebug( boolean debug ) {

this.debug = debug;

}

public void debug( String message ) {

if( debug ) { // only print if debug is true

pw.println( "DEBUG: " + message );

pw.flush();

}

}

public void error( String message ) {

// always print out errors

pw.println( "ERROR: " + message );

pw.flush();

}

}

Factory Pattern

public class ConsoleDebugger implements Trace {

private boolean debug;

public void setDebug( boolean debug ) {

this.debug = debug;

}

public void debug( String message ) {

if( debug )

System.out.println( "DEBUG: " + message );

}

}

public void error( String message ) {

// always print out errors

System.out.println( "ERROR: " + message );

}

}

With the use of a factory pattern, to use one of these debugger classes you would need to create it explicitly in your client program, as in

ConsoleDebugger cd = new ConsoleDebugger();

cd.setDebud(true);

. . .

cd.debug(“some message”);

cd.error(“another message”);

. . .

If you wanted to run the FileDebugger in your client, you would have to make changes to all the references to ConsoleDebugger to FileDebugger. This would have to happen in all client code.

Factory Pattern

A factory class for the Dubegger implementers could look like this

public class DebuggerFactory {

public static Debugger getDebugger() {

return new FileDebugger();

}

}

Alternatively, you can send some information to the factory method

public class DebuggerFactory {

public static Debugger getDebugger(String type) {

if (type == null)

return new BasicDebugger();

if (type.equals(Debugger.FILE)

return new FileDebugger();

else if (type.equals(Debugger.CONSOLE)

return new ConsoleDebugger();

else

return null; }

}

-----------------------

UI Object

Model

DefaultButtonModel

ButtonModel

implementation

JButton

dependency

renderer

actual element

rendered element

JTable

getColumnModel() --> TableColumnModel

getColumn(i) --> TableColumn

setCellEditor()

setCellRenderer()

setHeaderRenderer()

getCellEditor()

getCellRenderer()

getHeaderRenderer()

Custom Editor

Custom Renderer

CellEditor

void addCellEditorListener(CellEditorListener)

void cancelCellEditing()

Object getCellEditorValue()

boolean isCellEditable(EventObject anEvent)

void removeCellEditorListener(CellEditorListener)

boolean shouldSelectCell(EventObject anEvent)

boolean stopCellEditing()

TableCellEditor

Component getTableCellEditorComponent(JTable,Object,boolean , int , int )

AbstractCallEditor

void addCellEditorListener(CellEditorListener)

void cancelCellEditing()

protected void fireEditingCanceled()

protected void fireEditingStopped()

CellEditorListener[] getCellEditorListeners()

boolean isCellEditable(EventObject)

void removeCellEditorListener(CellEditorListener)

boolean shouldSelectCell(EventObject)

boolean stopCellEditing()

CellEditor

TableCellEditor

AbstractCellEditor

DefaultCellEditor

MyTableCellEditor

MyTableCellEditor

Component getTableCellEditorComponent(JTable,Object,boolean , int , int )

Object getCellEditorValue()

Default classes are always concrete

ListModel

void addListDataListener(ListDataListener)

Object getElementAt(int index)

int getSize()

void removeListDataListener(ListDataListener)

AbstractListModel

void addListDataListener(ListDataListener l)

protected void fireContentsChanged(Object source, int index0, int index1)

protected void fireIntervalAdded(Object source, int index0, int index1)

protected void fireIntervalRemoved(Object source, int index0, int index1)

ListDataListener[] getListDataListeners()

EventListener[] getListeners(Class listenerType)

void removeListDataListener(ListDataListener l)

DefaultListModel

void add(int index, Object element)

void addElement(Object obj)

int capacity()

void clear()

boolean contains(Object elem)

void copyInto(Object[] anArray)

Object elementAt(int index)

Enumeration elements()

void ensureCapacity(int minCapacity)

Object firstElement()

Object get(int index)

Object getElementAt(int index)

int getSize()

int indexOf(Object elem)

int indexOf(Object elem, int index)

void insertElementAt(Object obj, int index)

boolean isEmpty()

Object lastElement()

int lastIndexOf(Object elem)

int lastIndexOf(Object elem, int index)

Object remove(int index)

void removeAllElements()

boolean removeElement(Object obj)

void removeElementAt(int index)

void removeRange(int fromIndex, int toIndex)

Object set(int index, Object element)

void setElementAt(Object obj, int index)

void setSize(int newSize)

int size()

Object[] toArray()

String toString()

void trimToSize()

label.setTransferHandler(new TransferHandler("text"));

MouseListener listener = new DragMouseAdapter();

label.addMouseListener(listener);

private class DragMouseAdapter extends MouseAdapter {

public void mousePressed(MouseEvent e) {

JComponent c = (JComponent)e.getSource(); // label reference

TransferHandler handler = c.getTransferHandler();

handler.exportAsDrag(c, e, TransferHandler.COPY_OR_MOVE);

} }

public boolean importData (JComponent c, Transferable t) {

if (hasColorFlavor(t.getTransferDataFlavors())) {

try {

Color col = (Color)t.getTransferData(colorFlavor);

if (getChangesForegroundColor()) { c.setForeground(col); }

else {

c.setBackground(col); }

return true;}

catch (UnsupportedFlavorException ufe) {

System.out.println("importData: unsupported data flavor");}

catch (IOException ioe) {

System.out.println("importData: I/O exception"); }

} // if

return false;

}

Very important for export to trigger DnD

Do the actual DnD work

IMPORTANT for export.. This interface is created on init of export. Method getTransferData called on import.

Specifies DnD field. Important for Mime type by superclass

class MyMouseListener extends MouseAdapter {

public void mousePressed (MouseEvent e) {

if (e.getButton() == MouseEvent.BUTTON3)

popMenu.show(JPopupMenuTest.this.pane,

e.getX(),e.getY()); } }

The show method renders the JPopupMenu

1

2

3

4

5

6

7

8

0, 1, 2, … 99

Make the “C” button clear the display

private JButton getJButton() {

if (jButton == null) {

jButton = new JButton("Start");

jButton.addActionListener(new java.awt.event.ActionListener() {

public void actionPerformed(java.awt.event.ActionEvent e) {

int k = 0;

for (int q = 1; i < 100; q++) {

jTextField.setText(""+(i++));

jTextField.paint(jTextField.getGraphics());

for (int j = 0; j < 10000; j++)

System.out.println("qqq"); }}});

}

return jButton; }

private JButton getJButton1() {

if (jButton1 == null) {

jButton1 = new JButton("Stop");

jButton1.addActionListener(new java.awt.event.ActionListener() {

public void actionPerformed(java.awt.event.ActionEvent e) {

i = 100; }});

}

return jButton1; }

Start Handler

Stop Handler

public void actionPerformed(java.awt.event.ActionEvent e) {

Method method = null;

try {

method = EventGUI2.this.getClass().getMethod("startButtonAction",(Class[]) null);}

catch(NoSuchMethodException nsm){ System.out.println("No such method exception!"); }

new EventRunner(EventGUI2.this,method).start(); }

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

public class EventRunner extends Thread {

Object parent;

Method method;

public EventRunner(Object parent, Method method){

this.parent = parent;

this.method = method; }

public void run() {

try {

method.invoke(parent, (Object[])null); }

catch (InvocationTargetException ite) { System.out.println("Error1 on invoke()"); }

catch (IllegalAccessException iae) { System.out.println("Error2 on invoke()"); }}

} // EventRunner

public interface Subject {

public void addObserver(Observer o );

public void removeObserver(Observer o );

}

public interface Observer {

public void update(Subject s );

}

Factory method

A new type of implementer has been added!

We can use try catch to return one of a set of possible implementers

The model is maintained in the Vectors

JTable

The Table Model

Data is manipulated through the model and visualized through the table

Colors node minimized

tree.addTreeExpansionListener(new TreeExpansionListener() {

public void treeExpanded(TreeExpansionEvent e) {

System.out.println("You expanded: " + e.getPath().getLastPathComponent()); }

public void treeCollapsed(TreeExpansionEvent e) {

System.out.println("You collapsed: " + e.getPath().getLastPathComponent()); }

});

class TreeListener implements TreeSelectionListener {

public void valueChanged(TreeSelectionEvent e) {

DefaultMutableTreeNode node =

(DefaultMutableTreeNode) ((JTree)e.getSource()).getLastSelectedPathComponent();

/* nothing selected */

if (node == null) return;

/* retrieve the node that was selected */

Object nodeInfo = node.getUserObject();

/* Do something. */

System.out.println("You selected: " + nodeInfo); }

Setting a filter. See next page for filter code

# This is a comment BTW

NAME=Julius Dichter

JOB=professor

SCHOOL=University of Bridgeport

info.properties

Make modifications here to vary the layout look

yPane is the JTabbedPane

try LEFT, RIGHT, or BOTTOM

tooltip text

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download