|
The Java Specialists' Newsletter
Issue 045 2002-04-11
Category:
GUI
Java version: Multi-line cells in the JTableby Dr. Heinz M. Kabutz
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...
Upcoming Java Specialist Master Courses:
- please click here to sign up.
As from May 2010, we are also offering this course on the island of Crete. We
only accept 6 students per class in Crete, due to the size of our conference
room. Please book early to avoid disappointment!
San Jose CA, Mar 16-19 2010, $3500 Ottawa, Canada, Mar 22-25 2010, $3500 Oslo, Norway, Apr 13-16 2010, Kr 24500 Montreal, Canada, Apr 20-23 2010, $3500 Toronto, Canada, May 17-20 2010, $3500 Chania, Crete, May 25-28, Jun 29-Jul 2 or Aug 24-27 2010, €2500
In-house courses if these dates or locations do not suit you - click here for more information. 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
GUI Articles
Related Java Course
|