A Simple Pattern for Embedding Components into a Swing JTable

by Ilya 2. April 2010 06:01

If you read my last article, you might be wondering why I am blogging again about the same topic, in a different platform. In my work, I have decided to switch my project’s UI platform from SWT to Swing, and in doing so I had to repeat much of the same work.

I have to be honest, writing SWT is a lot more pleasant than writing Swing. It’s much more user-friendly, and the APIs are self-explanatory. However, as SWT uses the Java Native Interface (JNI) to call system-specific graphing API’s and is not bundled with the Java VM (like Swing is), each system version of your application has to ship with a different version of the SWT binary [for win, linux, mac32, mac64, etc.]. This may seem like not a big deal, but it becomes a big deal once the same code works for Windows and Linux, but the Table component has a memory leak in 64-bit Mac OS. Thus, let’s discuss Swing.

Java Swing in all its glory is an overcomplicated, poorly designed, outdated user interface platform. It’s only advantage comes in the fact that its bundled out of the box with the Java virtual machine, and it supports every OS that Java does. This is truly great. Also, the NetBeans UI designer coupled with some fancy SwingX components can really make your app look not too shabby. Let’s just be serious, there is no way I can generate that GridBagLayout (who named it that?) code myself, with 6 different integer state parameters to every call. Please understand that I compare Swing to WPF, to which it stands no chance. WPF is a modern well-designed interface designed to work on only one platform. WPF can also be written declaratively in XML, and to my knowledge, Swing cannot. Regardless, let’s get back to business.

As I was recreating my user-interface in Swing, I stumbled on this problem of embedding elements into a Swing JTable like this:

It actually took me quite a long time to figure out, and so I wanted to give you code on how to do it, so that you do not have to be bothered with this nightmare.

Understanding my Embed-Pattern

Basically, Swing tables allow different “TableEditor”s to handle all inputs into cells of a specific TableColumn. The TableEditor is responsible for two things: rendering a table cell when it is not in focus (interface TableCellRenderer) and handling edits while the cell is in focus (interface TableCellEditor). One way to embed a Swing component in a table is to build a class that implements TableCellRenderer and TableCellEditor, with a class signature likes this:

 

public class TableButton extends AbstractCellEditor implements TableCellEditor, TableCellRenderer

This class will keep track of ALL of these components in the column, which makes row removal and reordering complicated. I keep a hashtable of these components mapped by their row number. Like this example of embedded JButtons:

private Hashtable buttons;

Remember, that when you remove rows or reorder them, you need to delete them from this element, which I make possible with methods like:

public void removeRow(int row)
{
	if(buttons.containsKey(row))
	{
		buttons.remove(row);
	}
}

public void moveRow(int oldRow, int newRow)
{
	if(buttons.containsKey(oldRow))
	{
		JButton button = buttons.remove(oldRow);
		buttons.put(newRow, button);
	}
}

Also, there needs to be a way to report the user’s input on these components in the table. I use the java-simulated observer pattern for this:

public interface TableButtonPressedHandler
{
	/**
	 * Called when the button is pressed.
	 * @param row The row in which the button is in the table.
	 * @param column The column the button is in in the table.
	 */
	void onButtonPress(int row, int column);
}

public void addHandler(TableButtonPressedHandler handler)
{
	if (handlers != null)
	{
		handlers.add(handler);
	}
}

How To Use My Pattern

Now to the good stuff. I’ve implemented this pattern for JButton and JSlider which are attached in the source at the bottom of this arcticle, but you should be able to follow the pattern to make any other kind of embedded components such as JProgressBar or whatever else. The following is the code to use my methods appropriately:

Make the Column Editable in the TableModel to Receive Events:

On setup of the table model,

DefaultTableModel model = new DefaultTableModel(new String [] {"Slider", "Button" }, 0) 
{
		Class[] types = new Class[]
		{
			// the first argument is an integer because we want to provide the JSlider with an integer value of the current slider position
			// the second argument is a String because a button’s text is a String
			 java.lang.Integer.class, java.lang.String.class
		};

		public Class getColumnClass(int columnIndex)
		{
			return types[columnIndex];
		}

		boolean[] canEdit = new boolean [] 
{
	// remember that columns that employ my embed pattern must be editable
			true, true
		};

		public boolean isCellEditable(int rowIndex, int columnIndex) {
			return canEdit [columnIndex];
		}
};
table.setModel(model);

Add my Components to the Appropriate TableColumns

// sets the table columns to use the button/slider renderer and editor component
TableColumn sliderColumn = table.getColumnModel().getColumn(0);

TableSlider slider = new TableSlider(50 /*Default Slider Value out of 100*/);
slider.addHandler(new TableSlider.TableSliderMovedHandler() {
	
	@Override
	public void onSlide(int row, int column, int value) 
	{
		// handle the slide event
	}
});

// set the column's renderer and editor as the control
sliderColumn.setCellRenderer(slider);
sliderColumn.setCellEditor(slider);

When Removing or Reordering Rows

// when remove row index X
slider.removeRow(x);
// to move row at index y to index x
slider.moveRow(y, x);

 

Hope you guys enjoy this pattern and most of all, I hope it saves you time. 

SwingEmbeddingSource.zip (6.15 kb)

Tags: , , , , ,

Java

Comments

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading




About me

Hi, my name is Ilya. I am a student at MIT majoring in Course 6 - Computer Science and Electrical Engineering. During the school year, I work as a developer for LeanServer, a web platform optimization company. Right now, I am working as a Program Manager at Microsoft, for the Visual Studio team. I do a lot of development, which takes me to various technologies. If I find an interesting way to do something, I'll share it here. Enjoy.