Home > Articles > Programming > Java

Java Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

SwingX

Last updated Mar 14, 2003.

If youโ€™re looking for more up-to-date information on this topic, please visit our Java article, podcast, and store pages.

SwingX is a project available on the Sun web site that extends the desktop capabilities of Swing with features such as tree tables, auto-complete, tip of the day, collapsible panels, a date picker component, sorting, filtering, and highlighting for tables, trees, and lists, and much more. In this section I present examples of some of these components to help you enhance the look-and-feel of your Swing applications.

Source code and binary builds for SwingX components is available from SwingLabs. At the time of this writing, the latest version is 1.6.

If you are using Ant to build applications with SwingX, download the binary distribution from the web site, if you are using Maven then simply add the following dependency to your POM file:

    <dependency>
      <groupId>org.swinglabs</groupId>
      <artifactId>swingx</artifactId>
      <version>1.6</version>
    </dependency>

You can download the JavaDoc for the 1.6 milestone release or you can browse recent JavaDoc online for the SwingX weekly build.

Tree Tables

Tree tables are an interesting user interface component because they combine the hierarchical nature of a tree, but display table-like information for each of its nodes. Consider the most common example, a file browser: you may have a set of folders that represent directories and a set of files in those directories that are leaves in your tree. For each node you may want to report the size, the type, and the date that the file or folder was last modified. Figure 1 shows and example of such a tree table from my Ubuntu Nautilus file browser.

Figure 1. Ubuntu's Nautilus File Browser shows an example of a tree table

Surprisingly Sun never provided an implementation of a tree table natively in the Java libraries although you can find interesting articles about tree tables even prior to the formal inclusion of Swing into the Java SE JDK. SwingLabs created a JXTreeTable component and included it in the SwingX library that provides such functionality. In this section I demonstrate how to configure and use the JXTreeTable component in your applications.

Like all Swing components, the JXTreeTable separates its model from its visualization by defining the JXTreeTable visualization component and a TreeTableModel interface that tree table models must implement. The TreeTableModel classes define the data to be visualized and the JXTreeTable visualizes the data in the TreeTableModel.

For all practical purposes, you will most like derive a class from the AbstractTreeTableModel because it implements the TreeTableModel interface and provides helper methods for managing listeners and dispatching events. After extending AbstractTreeTableModel, you need to implement the following methods:

  • public int getColumnCount(): returns the total number of columns that you want to display in your table
  • public Object getValueAt(Object node, int column): returns the table cell value for the specified column for the specified node (where the node is a tree node)
  • public Object getChild(Object parent, int index): given a parent node, this method returns the child at the specified index (where the index is 0 to getChildCount() for the specified parent node)
  • public int getChildCount(Object parent): returns the number of children that the specified parent node has.
  • public int getIndexOfChild(Object parent, Object child): returns the numerical index of the child node in the list of the parent's child nodes
  • public boolean isLeaf(Object node): returns true if the specified node is a leaf or false if it is not (false if it has children or true if it does not and it should be painted as a node as opposed to a parent)

Rather than create a dynamic example using an exploratory model such as a file system, I decided to create a controlled model so that you can better understand how the JXTreeTable component interacts with the TreeTableModel. Thus the example defines a simple tree structure: a node has a name, a description, and a list of child nodes. If a node does not have children then it is assumed to be a leaf.

In listing 1, I create a TreeTableModel class named MyTreeTableModel that extends AbstractTreeTableModel and implements the required methods. It also contains an the internal MyTreeNode class that contains the aforementioned name, description, and list of child MyTreeNodes.

Listing 1. MyTreeTableModel.java

package com.geekcap.swingx.treetable;

import java.util.ArrayList;
import java.util.List;

import org.jdesktop.swingx.treetable.AbstractTreeTableModel;

public class MyTreeTableModel extends AbstractTreeTableModel 
{
	private MyTreeNode myroot;
	
	public MyTreeTableModel()
	{
		myroot = new MyTreeNode( "root", "Root of the tree" );
		
		myroot.getChildren().add( new MyTreeNode( "Empty Child 1", "This is an empty child" ) );
		
		MyTreeNode subtree = new MyTreeNode( "Sub Tree", "This is a subtree (it has children)" );
		subtree.getChildren().add( new MyTreeNode( "EmptyChild 1, 1", "This is an empty child of a subtree" ) );
		subtree.getChildren().add( new MyTreeNode( "EmptyChild 1, 2", "This is an empty child of a subtree" ) );
		myroot.getChildren().add( subtree );
		
		myroot.getChildren().add( new MyTreeNode( "Empty Child 2", "This is an empty child" ) );
		
	}

	@Override
	public int getColumnCount() 
	{
		return 3;
	}
	
	@Override
	public String getColumnName( int column )
	{
		switch( column )
		{
		case 0: return "Name";
		case 1: return "Description";
		case 2: return "Number Of Children";
		default: return "Unknown";
		}
	}

	@Override
	public Object getValueAt( Object node, int column ) 
	{
		System.out.println( "getValueAt: " + node + ", " + column );
		MyTreeNode treenode = ( MyTreeNode )node;
		switch( column )
		{
		case 0: return treenode.getName();
		case 1: return treenode.getDescription();
		case 2: return treenode.getChildren().size();
		default: return "Unknown";
		}
	}

	@Override
	public Object getChild( Object node, int index ) 
	{
		MyTreeNode treenode = ( MyTreeNode )node;
		return treenode.getChildren().get( index );
	}

	@Override
	public int getChildCount( Object parent ) 
	{
		MyTreeNode treenode = ( MyTreeNode )parent;
		return treenode.getChildren().size();
	}

	@Override
	public int getIndexOfChild( Object parent, Object child ) 
	{
		MyTreeNode treenode = ( MyTreeNode )parent;
		for( int i=0; i>treenode.getChildren().size(); i++ )
		{
			if( treenode.getChildren().get( i ) == child )
			{
				return i;
			}
		}

		// TODO Auto-generated method stub
		return 0;
	}
	
	 public boolean isLeaf( Object node )
	 {
		 MyTreeNode treenode = ( MyTreeNode )node;
		 if( treenode.getChildren().size() > 0 )
		 {
			 return false;
		 }
		 return true;
	 }
	 
	 @Override
	 public Object getRoot()
	 {
		 return myroot;
	 }
}

class MyTreeNode
{
	private String name;
	private String description;
	private List<MyTreeNode> children = new ArrayList<MyTreeNode>();
	
	public MyTreeNode() 
	{
	}
	
	public MyTreeNode( String name, String description ) 
	{
		this.name = name;
		this.description = description;
	}
	
	public String getName() 
	{
		return name;
	}
	
	public void setName(String name) 
	{
		this.name = name;
	}
	
	public String getDescription() 
	{
		return description;
	}
	
	public void setDescription(String description) 
	{
		this.description = description;
	}
	
	public List<MyTreeNode> getChildren() 
	{
		return children;
	}
	
	public String toString()
	{
		return "MyTreeNode: " + name + ", " + description;
	}
}

The constructor creates a MyTreeNode root element and then populates it with an empty node, followed by a sub-tree with two elements, and finally another empty element. I've defined three columns: the name of the node, the description of the node, and the number of children in the node (as controlled by the getColumnCount() and getColumnName() methods.) The getRoot() method returns the root of the tree as an Object and the other methods cast that Object to a MyTreeNode object for analysis:

  • getValueAt() returns the table cell for the specified column in the specified node
  • getChild() returnsthe indexed child of the specified node
  • getChildCount() method returns the number of children in the MyTreeNode's ArrayList of children
  • getIndexOfChild() iterates over the node's children and returns the matching index
  • isLeaf() returns true if the node does not have any children (the size of its ArrayList is 0) or false if it does have children

The MyTreeNode class should be self-explanatory: it maintains the node's name, description, and list of children as an ArrayList of MyTreeNode elements.

Listing 2 demonstrates how to create an instance of the JXTreeTable class with the MyTreeTableModel class hosting its data. I hope to add more examples in the SwingXExample class as I develop more articles in this series, which is why the JXTreeTable is presented in a JTabbedPane.

Listing 2. SwingXExample.java

package com.geekcap.swingx;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;

import org.jdesktop.swingx.JXTreeTable;

import com.geekcap.swingx.treetable.MyTreeTableModel;

public class SwingXExample extends JFrame 
{
	private JTabbedPane tabs = new JTabbedPane();
	
	private MyTreeTableModel treeTableModel = new MyTreeTableModel();
	private JXTreeTable treeTable = new JXTreeTable( treeTableModel );
	
	public SwingXExample()
	{
		super( "SwingX Examples" );
		
		// Build the tree table panel
		JPanel treeTablePanel = new JPanel( new BorderLayout() );
		treeTablePanel.add( new JScrollPane( treeTable ) );
		tabs.addTab( "JXTreeTable", treeTablePanel );
		
		// Add the tabs to the JFrame
		add( tabs );
		
		setSize( 1024, 768 );
		Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
		setLocation( d.width / 2 - 512, d.height/2 - 384 );
		setVisible( true );
		setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
	}
	
	
	public static void main( String[] args )
	{
		AppStarter starter = new AppStarter( args );
		SwingUtilities.invokeLater( starter );
	}
}

class AppStarter extends Thread
{
	private String[] args;
	
	public AppStarter( String[] args )
	{
		this.args = args;
	}
	
	public void run()
	{
		SwingXExample example = new SwingXExample();
	}
	
}

Listing 2 creates an instance of the MyTreeTableModel class and then immediately creates a JXTreeTable class instance that hosts the MyTreeTableModel. The rest of the class is boilerplate Swing code to create a top-level window (JFrame), configure it to close itself on exit, and to set its size, position, and visibility.

If you want to add additional interactivity to your JXTreeTable, it fires TreeSelectionListener, TreeExpansionListener, and TreeWillExpandListener events that you can access by invoking one of the JXTreeTable's addXXListener() methods. Figure 2 shows a screen shot of the SwingXExample class displaying the JXTreeTable.

Figure 2. SwingXExample class showing an example of the JXTreeTable class

Summary

SwingLabs provides a set of Swing components in its SwingX library that enhance the visualization of Java desktop applications. Thus far in this series I have presented the JXTreeTable class that provides support for a tree-table hybrid component that presents detailed (table) information grouped together into a hierarchical structure (tree). In future installments I will provide examples using other SwingX components.