The Java Specialists' Newsletter [Issue 045] - Multi-line cells
in the JTable
Author: Dr. Heinz M. Kabutz
JDK version:
Category: GUI
You can subscribe from our home page:
http://www.javaspecialists.co.za (which also hosts all previous issues,
available free of charge
Welcome to the 45th edition of The Java(tm) Specialists' Newsletter,
read in over 78
countries, with newest additions Thailand and Iceland. Both end with "land"
but they couldn't be two more opposite countries. Why don't those drivers who
insist on crawling along the German Autobahn at 140km/h stick to the slow lane?
I drove almost 500km on Friday, and had the opportunity to meet Carl Smotricz (my
archive keeper) and some other subscribers in Frankfurt. We had some very
inspiring discussions regarding Java performance, enjoyed some laughs at Java's
expense and listened to my tales of life in South Africa.
Unsubscription Fees: Some of my readers wrote to tell me what a
fantastic idea unsubscription fees were to make some money. Others wrote angry
notes asking how I had obtained their credit card details. All of them were
wrong! Note the date of our last newsletter - 1st April! Yes, it was all part of
the April Fool's craze that hits the world once a year. Apologies to
those of you who found that joke in poor taste (my wife said I shouldn't put it
in, but I didn't listen to her). The rest of the newsletter was quite genuine. A
friend, who was caught "hook, line & sinker", suggested that I should clear
things up and tell you exactly what my purpose is in publishing "The Java(tm)
Specialists' Newsletter":
#1. Publishing this newsletter is my hobby: No idealism here at all. A
friend encouraged me a few years ago to write down all the things I had been
telling him about Java, so one day I simply started, and I have carried on doing
it. It's a great way to relax, put the feet up and think a while.
#2. There are no subscription / unsubscription fees: The day that I'm
so broke that I need to charge you for reading the things I write, will be the
day that I immediately start looking for work as a permanent employee again.
There are neither subscription nor unsubscription fees, nor will there ever be.
#3. How do I earn my living? Certainly not by writing
newsletters! I spend about 75% of my time writing Java code on contract for
customers situated in various parts of the world. 20% of my time is spent
presenting Java and Design Patterns courses in interesting places such as
Mauritius and South Africa and the last 5% is spent advising companies about
Java technology.
#4. Marketing for Maximum Solutions: Because people know
my company and me through this
newsletter, I have received many requests for courses, contract work and
consulting, and this helps me to make a living. My hobby of writing the
newsletter has turned out to have some nice side effects.
And now, without wasting any more time, let's look at a real-life Java
problem...
Multi-line cells in the JTable
The last slide of all my courses says that my students may send me questions
any time they get stuck. A few weeks ago Robert Crida from Peralex in
Bergvliet, South Africa, who came on my
Java course
last year, asked me how to display a JTextArea within a cell of a JTable. I
sensed it would take more than 5 minutes to answer and being in a rush to finish
some work inbetween Mauritius and Germany, I told him it would take me a few
days to get back to him. When I got to Germany, I promptly forgot about his
problem, until one of his colleagues reminded me last week.
Robert was trying to embed a JTextArea object within a JTable. The behaviour
that he was getting was that when he resized the width of the table, he could
see that the text in the text area was being wrapped onto multiple lines but the
cells did not become higher to show those lines. He wanted the table row height
to be increased automatically to make the complete text area visible.
He implemented a JTextArea cell renderer as below:
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.table.TableCellRenderer;
public class TextAreaRenderer extends JTextArea
implements TableCellRenderer {
public TextAreaRenderer() {
setLineWrap(true);
setWrapStyleWord(true);
}
public Component getTableCellRendererComponent(JTable jTable,
Object obj, boolean isSelected, boolean hasFocus, int row,
int column) {
setText((String)obj);
return this;
}
}
I wrote some test code to try this out. Before I continue, I need to point
out that I use the SUN JDK 1.3.1 whereas Robert uses the SUN JDK 1.4.0.
The classic "write once, debug everywhere" is a topic for another newsletter ...
import javax.swing.*;
import java.awt.BorderLayout;
public class TextAreaRendererTest extends JFrame {
// The table has 10 rows and 3 columns
private final JTable table = new JTable(10, 3);
public TextAreaRendererTest() {
// We use our cell renderer for the third column
table.getColumnModel().getColumn(2).setCellRenderer(
new TextAreaRenderer());
// We hard-code the height of rows 0 and 5 to be 100
table.setRowHeight(0, 100);
table.setRowHeight(5, 100);
// We put the table into a scrollpane and into a frame
getContentPane().add(new JScrollPane(table));
// We then set a few of the cells to our long example text
String test = "The lazy dog jumped over the quick brown fox";
table.getModel().setValueAt(test, 0, 0);
table.getModel().setValueAt(test, 0, 1);
table.getModel().setValueAt(test, 0, 2);
table.getModel().setValueAt(test, 4, 0);
table.getModel().setValueAt(test, 4, 1);
table.getModel().setValueAt(test, 4, 2);
}
public static void main(String[] args) {
TextAreaRendererTest test = new TextAreaRendererTest();
test.setSize(600, 600);
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.show();
}
}
You'll notice when you run this, that when the row is high enough the text
wraps very nicely inside the JTextArea, as in cell (0, 2). However,
the JTable does not increase the row height in cell (4, 2) just because you
decide to put a tall component into the cell. It requires a bit of prodding to
do that.
My first approach was to override getPreferredSize() in the
TextAreaRenderer class. However, that didn't work because JTable didn't take
your preferred size into account in sizing the rows. I spent about an hour
delving through the source code of JTable and JTextArea.
After a lot of experimentation, I found out that JTextArea actually
had the correct preferred size according to the width of the column in the
JTable. I tried changing the getTableCellRendererComponent()
method:
public Component getTableCellRendererComponent(JTable jTable,
Object obj, boolean isSelected, boolean hasFocus, int row,
int column) {
setText((String)obj);
table.setRowHeight(row, (int)getPreferredSize().getHeight());
return this;
}
On first glimpse, the program seemed to work correctly now, except that my
poor CPU was running at 100%. The problem was that when you set the row height,
the table was invalidated and that caused getTableCellRendererComponent()
to be called in order to render all the cells again. This in turn then set the
row height, which invalidated the table again. In order to put a stop to this
cycle of invalidation, I needed to check whether the row is already the correct
height before setting it:
public Component getTableCellRendererComponent(JTable jTable,
Object obj, boolean isSelected, boolean hasFocus, int row,
int column) {
setText((String)obj);
int height_wanted = (int)getPreferredSize().getHeight();
if (height_wanted != table.getRowHeight(row))
table.setRowHeight(row, height_wanted);
return this;
}
I tried it out (on SUN JDK 1.3.1) and it worked perfectly. There are
some restrictions with my solution:
- It will only work when only one column contains the TextAreaRenderer.
You can easily write around this problem by having coordination between the
various TextAreaRenderers.
- You need to implement a TextAreaEditor, which I imagine will actually be
a lot easier.
Satisfied, I sent off the answer to Robert, with the words: "After spending
an hour tearing out my hair, I found a solution for you, it's so simple you'll
kick yourself, like I did myself "
A few hours the answer came back: "Your solution does not solve my problem at
all."
"What?" I thought. Upon questioning his configuration, we realised that I was
using JDK 1.3.1 and Robert was using JDK 1.4.0. I tried it on JDK 1.4.0 on my
machine, and truly, it did not render properly! What had they changed so that it
didn't work anymore? After battling for another hour trying to figure out what
the difference was and why it didn't work out, I gave up and carried on with my
other work of tuning someone's application server. If you know how to do it in
JDK 1.4.0, please tell me!
I have avoided JDK 1.4 for real-life projects, because I prefer others to
find the bugs first. Most of my work is spent programming on real-life projects,
so JDK 1.3.1 is the version I'm stuck with. My suspicion of new JDK versions
goes back to when I started using JDK 1.0.x, JDK 1.1.x, JDK 1.2.x. I found that
for every bug that was fixed in a new major version, 3 more appeared, and I grew
tired of being a guinea pig. I must admit that I'm very happy with JDK 1.3.1, as
I was with JDK 1.2.2 and JDK 1.1.8. I think that once JDK 1.4.1 is released I'll
start using it and then you'll see more newsletters about that version of Java.
In a future newsletter I will demonstrate how you can implement "friends" at
runtime in the JDK 1.4.
Heinz
Copyright 2000-2004 Maximum Solutions, South Africa
Reprint Rights. Copyright subsists in all the material included
in this email, but you may freely share the entire email with anyone you feel
may be interested, and you may reprint excerpts both online and offline provided
that you acknowledge the source as follows: This material from The Java(tm)
Specialists' Newsletter by Maximum Solutions (South Africa). Please contact
Maximum Solutions for more
information.
Java and Sun are trademarks or registered trademarks of Sun Microsystems,
Inc. in the United States and other countries. Maximum Solutions is independent
of Sun Microsystems, Inc.
9503 bytes more | comments? | | Score: 4
|