Win a copy of Programmer's Guide to Java SE 8 Oracle Certified Associate (OCA) this week in the OCAJP forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

TableRenderer with multiple lines of text

 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm displaying a JTable in a modal JDialog, one of the fields is an address. I want to show the address as two to three lines, just as it would appear on a letter. I created a custom TableCellRenderer that uses a JTextArea and it seems to work if I insert the line breaks.

The problem is the row height doesn't change. I'm not sure what to do with this. Each row may have a different height as it could be anywhere from 1 to 3 lines of text (1 if there is no address to show, 3 if there's two address lines plus the city, state zip).

Furthermore, as a question on top of that. What's the best way to make the JDialog resize to show x number of rows? The size it needs to be to accomodate this is going to change as the user scrolls through them, or moves through with keys, as the row heights will vary.
 
H Lander
Greenhorn
Posts: 15
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The height of each row in a JTable is by default fixed (16 pixels). In your table you have to specify the height of each row using the setRowHeight(int row, int rowHeight) method. If you know how many lines are in the row, then you can probably do this from the TableCellRenderer (not sure though as I have not actually done it).

I would recommend against the idea of changing the height of the dialog as the user scrolls down the table. It would probably not work well (other than to irritate the user of your dialog).

Good Luck,
Henry
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm not sure how to ascertain the appropriate row height. Can I just set it to the height of the JTextArea being used? I don't think that would work as the borders need to be included don't they?
 
H Lander
Greenhorn
Posts: 15
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You can do the following on the Text Area:
theTextArea.getPreferredSize().height
That takes into account the space required for the borders.

However, I wouldn't recommend that as it would slow down the painting of the table. I would recommend, that you find out how much height each line requires by trial and error. Assuming that 18 pixels is appropriate, then set the row height to either 18, 36, or 54 based on the number of lines in the address. If the Text Area has a border, then add the space required at the top and bottom for the border.

Good Luck,
Henry
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Trial and error isn't a solution. If I do that the results are unpredictable when used on different operating systems with different look and feels or with a different font or font size.
 
Michael Dunn
Ranch Hand
Posts: 4632
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
this might be worth a read

http://www.javaspecialists.co.za/archive/Issue106.html
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Michael: I'm printing it out now so I can read it back at my desk but at a quick glance it looks like exactly what I needed. Where do you find these gems?
 
Michael Dunn
Ranch Hand
Posts: 4632
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
these ones I subscribe to - they are always a good (and informative) read
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yeah, I don't like his code in this particular instance, it seems needlessly inefficient in many places. However, he seems to have a pretty darn good head on his shoulders and his methodology gave me some of the ideas/answers I needed. I will post my solution, or at least an incarnation of it, when I'm done for anyone interested.
 
Alejandro Barrero
Ranch Hand
Posts: 339
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
For some strange reason, the solution is to use a scroll box that contains a text area. here is the code:


import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;

import javax.swing.BorderFactory;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.table.TableCellRenderer;
/**
* This class is a JTextArea renderer for JTables. Traditionally, the idea did
* not work because the preferred size for the JTextArea returned at line<p>
* jTable.setRowHeight(row, (int)getPreferredSize().getHeight());<p>
* was the height of the column. Fortunately, for some reason, everything works
* if the renderer extends JScrollPane.
*
*/
public class TextAreaRenderer extends JScrollPane implements TableCellRenderer {
JTextArea jTextArea = new JTextArea();
/**
* Constructor.
*/
public TextAreaRenderer() {
this.jTextArea.setLineWrap(true);
this.jTextArea.setWrapStyleWord(true);
this.jTextArea.setFont(new Font("Dialog", Font.PLAIN, 12));
setBorder(BorderFactory.createEmptyBorder());
setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
getVerticalScrollBar().setFocusable(false);
getHorizontalScrollBar().setFocusable(false);
setViewportView(this.jTextArea);
}

/**
* @see
* javax.swing.table.TableCellRenderer#getTableCellRendererComponent(javax.swing.JTable,
* java.lang.Object, boolean, boolean, int, int)
*/
public Component getTableCellRendererComponent(JTable jTable, Object obj,
boolean isSelected, boolean hasFocus, int row, int column) {
//Get the length of the text.
String displayString = (String)obj;
this.jTextArea.setText(displayString);
FontMetrics fontMetrics = this.jTextArea.getFontMetrics(this.jTextArea.getFont());
int textLength = SwingUtilities.computeStringWidth(fontMetrics, displayString);
//Compute the height.
int width = jTable.getColumnModel().getColumn(column).getWidth();
int numberOfRows = textLength/width;
if (numberOfRows*width < textLength)
numberOfRows++;
this.jTextArea.setRows(numberOfRows);
int rowHeight = (int)getPreferredSize().getHeight();
if (jTable.getRowHeight(row) < rowHeight)
jTable.setRowHeight(row, rowHeight);
return this;
}
}
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That won't work very well and not in the way I need it to. For one thing, the row can never get smaller. What happens if a user expands the largest column? The row should be smaller, but it won't be. I traced JTextArea's odd behavior down to BasicTextUI and some other behind the scenes Swing classes. Basically, when the preferred size hasn't been calculated yet it ends up looking at the size, which usually hasn't been set yet either. The natural inclination is the set the size to the width and leave height at 0, but that won't work because the offending class checks both height and width and uses NEITHER if EITHER are <= 0. This leads to an incorrect preferred size being calculated. This is the reason why setSize(columnWidth, Integer.MAX_VALUE) will work. Actually, setSize(columnWidth, insets.top + insets.bottom + 1) will work as well, it just needs to be taller than it's insets and it will function properly. JScrollPane likely invokes code that ends up in a similar workaround happening.

This might even be a bug, I'm not sure and I don't have the time or inclination to see if this behavior was intended or not. I would have thought it'd still use the width if it were set even if the height weren't, and not discard both if either was too small. Maybe I'll have time to investigate the matter further some day, but at this ponit I'm just happy to have it done and working, even if I don't like the workaround.

Here's what I came up with. Note that this impelements alternating color schemes and fonts, which was something I needed and not directly relevent to this. It also suppors having multiple renderers in the same table because I needed some columsn to have specific row counts and wanted them truncated if they were too small for their text, others needed to wrap. Hence the need for a third party to coordiante their heights to determine the appropriate row height for the table.
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator


[ October 27, 2005: Message edited by: Ken Blair ]
[ October 27, 2005: Message edited by: Ken Blair ]
 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic