This section describes the design and implementation of the matrix demonstration tool used in an earlier lab and assignment. You can run the tool by executing this Matrix.jar file.

Recall that Matrix is an interface type that is implemented by the AbstractMatrix class which is itself extended by the ArrayImplementation and ArrayListImplementation concrete classes. (See Encapsulation and Abstraction on the menu at left.)

Client classes that use matrices need only type their variables with the Matrix type. Thus, at the center of our matrix GUI design we have the Matrix interface type:

We start by considering the display of a matrix with the ability of the user to input element values, as shown below.

Note that all elements of the display, whether a row/column number or a matrix element, are arranged in a grid.

We will call the class that lays out the matrix display MatrixDisplay. It has two primary relationships: We model the first relationship using aggregation between MatrixDisplay and Matrix.

We model the second relationship by having MatrixDisplay extend the GridPane class from the Java library:

Since multiple instances of row/column numbers (headers) and matrix elements need to be displayed, it is convenient to define classes for them.

Since these classes will only be used by MatrixDisplay, we make them inner classes, i.e. private to MatrixDisplay:

To control a matrix is to fill it with elements, create an identity, clear it, or transpose it.

We make these options, as well as creating a new matrix, available as shown below. The user has clicked on an Actions choice box to reveal matrix control options.

We will call the class that provides matrix controls MatrixControl. It lays out two parts, arranged vertically: Thus a MatrixControl object depends on the Matrix type and contains a MatrixDisplay object.

We get vertical orientation of the components by extending the VBox class from the Java library:

When the user elects to create a new matrix, a choice of dimensions is given using integer spinners:

Since the spinners are identical in function, we define an inner class called LabeledSpinner, two instances of which are contained in the MatrixControl object.

A labeled spinner is laid out as a vertical arrangement of a label and a spinner, so LabeledSpinner also extends VBox:

To demonstrate matrix operations, we need two matrix operands, a choice of operations, and a matrix displayed for the result.

Here, a matrix multiplication has been performed:

We will call the class that provides matrix operations MatrixOperation. It lays out four parts, arranged horizontally: We get horizontal orientation of the components by extending the HBox class from the Java library:

Since the four objects are labeled scene nodes, we define an inner class called LabeledNode, four instances of which are contained in the MatrixOperation object.

A labeled node is laid out as a vertical arrangement of a label and a node, so LabeledNode also extends VBox:

To this point, we have been concerned primarily with layout issues.

In this section we address how our GUI classes use listeners and properties to dynamically change aspects of the display as the user interacts with it.

The primary interactions are: Handling these are straightforward using the addListener method for choice box value properties.

A MatrixControl object has a choice box called actionChoices that allows the user to perform matrix actions. We add a listener for actions like this:

Similarly, the MatrixOperation object has a choice box called opChoices that allows the user to perform matrix operations. We add a listener for operations like this:

There are other interactions that also affect the display. Specifically:
Listening for matrix actions is straightforward, as we simply need to add a listener to theMatrixControl object's actionChoices choice box.

Since the MatrixOperation object does the listening, MatrixControl provides a getter for the choice box's value property:

Note the return type ObservableValue. This is an interface type that contains the addListener method.

The MatrixOperation object must listen so that it can clear the result matrix when either of the operand matrices has an action taken on it. Since the MatrixOperation object creates and contains the two MatrixControl objects mc1 and mc2, it uses getActionProperty to add listeners that clear the result when any actions occur:

To indicate when the user has manually edited a matrix, we add a SimpleBooleanProperty instance field to MatrixDisplay:

In the constructor, we initialize it:

We add a listener to each text field representing a matrix element that toggles the boolean property each time it is edited:

Now we make the property available to outside classes through a getter:

Since the MatrixControl object creates and contains a MatrixDisplay object display, MatrixControl makes display's edited property available through a getter of its own:

The SimpleBooleanProperty class implements the ObservableValue interface.

The MatrixOperation object must listen so that it can clear the result matrix when either of the operand matrices is edited:

Beyond simply listening for whether a matrix element has been edited or modified through an action, we also need to know when a matrix's dimensions have changed, for example, through transposing or creating an entirely new matrix.

In that case we need to:

For the actions, a listener is not needed; we can simply update the actions when a new matrix is created.

For the operations, we introduce into MatrixControl an object property. Note the type parameter Matrix:

In the constructor, after the matrix has been created, it is wrapped in a SimpleObjectProperty:

SimpleObjectProperty implements the Property interface, which is a subinterface of ObservableValue.

By adding a listener to this property, we can detect when the entire matrix object has changed, not just when an element has been modified.

So that MatrixOperation can listen for changes to the property, we provide an accessor in MatrixControl:

In MatrixOperation we add code that updates the available operations