http://www.developer.com/

Back to article

Creating Interactive GUIs with Swing's MVC Architecture Part 2: Dynamic View for a Static Model


June 3, 2004

In the first part of this article, "Model-View-Controller and Swing," I described fundamentals of how a Model-View-Controller design pattern is embedded within a Swing toolkit. In this article, I'll show how to create a dynamically adjustable presentation of a corresponding model for a Swing Table UI component. The approach will also rely on inheritance, passing objects by reference, and event/listener mechanisms of the Java language. The final sample code will show a solution to the problem of real-time manipulation of a presentation without altering the model, or requesting data again on every user-invoked event. The technique can easily be adapted to any Swing component.

Sample Project

As I've indicated in my previous articles, MVC has been around for a long time and has been adapted in all sorts of frameworks, ranging from desktop GUIs to Web-based applications. To show how Model-View-Controller is used in the Java Swing toolkit, I've created a simple project with fictional population data on different moons of planets within our solar system. The data is presented in a Swing JTable component and I've included other components in my application as well. See Figure 1. If you are not familiar with Swing or Java AWT frameworks, please see the reference section for online and printed publications and tutorials on these technologies. In this article, I'll assume that you are familiar with some graphical toolkit, and are confident with Java as an object-oriented language, and perhaps have used Swing to create simple GUIs.

The application has a table consisting of Moon name, Planet name (it belongs to), its atmospheric gas name, and population size. The business objective of the UI will be to present the user with data, and let the user manipulate it in any way the user may choose; for instance, to sort based on the moon name or filter by planet. The application must also be fast and not memory intensive. And, it certainly must not require reloading of data or creation of new Model Objects on each user request! The application puts in use all the fundamentals I presented in the first part of this article and also contains an easily adaptable approach to the dynamic manipulation of the presentation. You should be able to apply my models and programming routines to your enterprise GUI application without major changes.

This is a screenshot of an application; the drop-down selection is a filtering mechanism.

Figure 1

Presentation Layer

To demonstrate visual manipulation of the data, my application has a drop-down menu (JComboBox) and a main table (JTable). The drop-down has a list of planets and serves as a filtering mechanism. the When user selects an entry from the list, the presentation in the table is changed. The table also has a default header, which serves as a sorting mechanism; clicking on any column header name will sort the entire table based on that column, thus also altering presentation. Both sorting and filtering are user requests, resulting in a change of what is displayed on the screen; the underlying data, however, is not changed. This is achieved by getting and keeping data in a model object and then altering how it's shown in a view object. Different user requirements may call for model object(s) with special characteristics; therefore, they have to be custom and either extend or change default model properties of that particular visual component.

Custom Model Defined

As I described in Part 1, all Swing objects follow a separable model architecture, and every visual component has a corresponding model object. In case of JTable, a javax.swing.table.DefaultTableModel object holds all data and defines essential methods on that data.

Such as:

getRowCount(),

getValueAt(int row, int col),

setValueAt(int row, int col),

isCellEditable(int row, int col) and many others including event methods.

A JTable object with a default model will let users do a lot, but it won't help with a dynamic change in presentation. In order to accomplish sorting, filtering, and any other operation on the view, a custom model object(s) have to be created. I have a PlanetTableModel object (that inherits everything from the DefaultTableModel), to hold the real data, and in addition several filtering models, PlanetTableSortFilter and PlanetDataExclutionFilter, to help with sorting and filtering tasks.

The filtering models do not hold data directly, but hold a reference to it, and they extend AbstractTableModel to make sure they are compliant with JTable's public model signature. Also, both of them implement the TableModelListener interface to deal with actual events raised from the users. The sorting Model was adapted from the Sun Java tutorial and modified to work with my application. See the Reference section.

When JTable is created, it already contains a default model, but I'm installing my own custom one, and will actually chain my filters on top of it. For the purposes of the simple application, I created a Moon class, See Listing 1, which contains data corresponding to one row of the table. In the initialization code, I created an array of Moon objects and populated it with data, and then set it to be the main data of my model.

static Moon[] moons =
   new Moon[] {
      new Moon("Moon", planet[1], "None", 0),
      new Moon("Metis", planet[2], "Ardon", 134000),
... ... ... ...
model.setModelData(moons)
... ... ... ...

The data in an enterprise application would most likely come from some relational data source or a dynamic data store, but in my case I simply populate the table from an array. The setModelData method is not part of the Table Model and is custom, piggybacking on the addRow(object[] array) method from DefaultTableModel.

See Listing 2 for the method's code.

In my custom model, I also have set header names and overwrote some methods; for instance, isCellEditable is set to always return false, and getColumnNane(int c) is set to use an array of column header names.

Then, the custom model could be assigned to the JTable, for example.

PlanetTableModel model = new PlanetTableModel();
JTable planetList      = new JTable(model);

This ensures that the displayed JTable uses the custom model and all calls from the view are directed to it. The table is then added to the Frame and validated. As you can see in Figure 1, Moon information is displayed, the header is correct, and all cells are not editable.

Model Chaining

Installing the custom model allows for much greater control over how data is handled when the user starts to interact with the presentation; however, chaining models (or installing filters) will allow for the manipulation of presenting without changing the data itself. As I've mentioned, the technique for doing so, relies on inheritance, passing objects by reference, and event/listener mechanisms. Inheritance is used when custom models sub-class default Model objects and override or extend default methods. But, the other two need more explaining.

The filter models have constructors that take a reference to the table model and do not actually hold any data. However, they do override indexing methods of the table model interface such as getValueAt(row, col) and hold an internal index of the data. This allows creating custom model, passing it by reference to the filter model and then installing filter model into the JTable. Therefore, the table uses indexing and locating methods of the filter to represent actual data, stored in a separate custom model object. And there could by more filters chained on top of each other.

model           = new PlanetTableModel();                      // model
exclusionFilter = new PlanetTableExclutionFilter(model);       // filter 1
sortFilter      = new PlanetTableSortFilter(exclusionFilter);  // filter 2
planetList      = new JTable(sortFilter);                      // table

Event Model and Dynamic Model Presentation

The event/listener mechanism is involved in propagating events to all interested models (because they implement TableModelListener), making sure proper action, such as recalculation of the index, is taken on user request.

public void tableChanged(TableModelEvent e) {    // called in table
                                                 // change
      reallocateIndexes();                       // do custom action
      fireTableChanged(e);                       // forward event
   }

See Listing 3 for the relocateIndexes() code.

For instance, the exclusion filter model has a custom method that takes a "filter" string; this method is initiated when the user selects an item from the drop-down list. Once the call is made, the filter recalculates the index to include only rows containing a matching string and generates an event that table has been changed. See Listing 4. Not coincidently, it has identical code to recalculate index as a reallocateIndexes() method.

This updates the presentation and the underlying data; the real model is not changed.

Figure 2

Clicking on the column header will call a sorting algorithm from the sorting model that also recalculates the index of presented (filtered) data and also generates an event that the table has been changed. This demonstrates an enormous power of chaining models as the presentation is now filtered and sorted, in real time, without additional object creation or repopulation of any model.

Figure 3

Sorting, of course, works on the complete, unfiltered data set; see Figure 4.

Figure 4

Conclusion

The technique presented in this article relies on the separable architecture of Swing. The source code has a full listing of the custom model and filtering models as well as main the GUI code. It undoubtedly can be applied to any Swing object and imbedded into any graphical application that requires real-time manipulation of a presentation. Custom filters can do any number of tasks and be easily added later on in the development cycles, preserving existing functionality and introducing new features.

Download Source Code

Listing 1

class Moon
{
   public String name;
   public String planet;
   public int population;
   public String atmosphere;

   Moon(String name, String planet, String atmosphere, int population)
   {
      this.name = name;
      this.planet = planet;
      this.population = population;
      this.atmosphere = atmosphere;
   }

   public String toString()
   {
      return this.name;
   }
}

Listing 2

public void setModelData(Moon[] data) {
   for (int i = 0; i < data.length; i++) {
      Moon moon = data[i];
      Object row[] = new Object[] {moon.name, moon.planet,
                                   moon.atmosphere,
                                   String.valueOf(moon.population)};
      this.addRow(row);
      }
      fireTableDataChanged();
   }
Note: The fireTableDataChanged() method is one of methods that indicate to the visual components to sync with the model and therefore refresh the presentation.

Listing 3

public synchronized void reallocateIndexes() {

   int rowCount = model.getRowCount();

   indexList.clear();

   // Iterate through the model

   for (int z = 0; z < rowCount; z++) {
      String planet = (String) model.getValueAt(z, 0);
      if (filter.equals("All")) {
         indexList.add(new Integer(z));
      } else if (filter.equals(planet))
         indexList.add(new Integer(z));
      }
   }

Listing 4

public synchronized void setFilter(String filter) {

   if (model instanceof PlanetTableModel)
   ((PlanetTableModel) model).setFilter(filter);
   this.filter = filter;
   int rowCount = model.getRowCount();
   indexList.clear();

   // Iterate through the model
   for (int z = 0; z < rowCount; z++) {
   String planet = (String) model.getValueAt(z, 1);
   if (filter.equals("All")) {
         indexList.add(new Integer(z));
         } else if (filter.equals(planet))
            indexList.add(new Integer(z));
      }

      // Tell the component that the data have changed
      this.fireTableDataChanged();
   }

About the Author

Vlad Kofman is a System Architect working on projects under government defense contracts. He has also been involved with enterprise-level projects for major Wall Street firms and the US government. His main interests are object-oriented programming methodologies and design patterns.

References

Core Swing Advanced Programming by Kim Topley ISBN: 0130832929. Publisher: Prentice Hall; December, 2000

Core Java 2, Volume II: Advanced Features (5th Edition) by Cay Horstmann and Gary Cornell. Publisher: Prentice Hall; December, 2001

A Swing Architecture Overview: http://java.sun.com/products/jfc/tsc/articles/architecture

Advanced MVC: http://www.ifi.unizh.ch/richter/Classes/oose2/03_mvc/02_mvc_java/02_mvc_java.html#4%20Models%20as%20Proxies

"Swing model filtering" by Mitch Goldstein: http://www-106.ibm.com/developerworks/java/library/j-filters/

Working with Models

Sorting and Otherwise Manipulating Data: http://java.sun.com/docs/books/tutorial/uiswing/components/table.html#sorting

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date