Three years ago, I played an April Fools' joke on my readers, as is customary in South Africa. However, with 109 countries on the subscription list, I have stopped playing the fool, since this is can cause confusion in cultures that do not celebrate the 1st of April. In a follow-up newsletter, I explained that the unsubscription fees were nothing but a joke.
Coincidentally, almost three years ago to the day, I wrote about Multi-line cells in the JTable. I discussed how to wrap text inside cells of a JTable. My solution only worked pre-JDK 1.4 , so I asked readers to send me a solution if they found it.
Since I wrote that letter, several readers have sent solutions that worked for JDK 1.4 and JDK 5.0: Scott Sauyet, Dave Combs, Hitesh Patel, Debbie Utley, Wolf Siberski, Dirk Hillbrecht, Andrew Cole and Sami. Thank you very much!
Most of the solutions consisted of adding one line to my code:
setSize(columnModel.getColumn(column).getWidth(), 100000);
This magically made the renderer work under JDK 1.4 and 1.5. However, it did not address the other issues that I mentioned in the original newsletter: - It worked when only one column contained the TextAreaRenderer.
- I did not implement a TextAreaEditor.
Here is a renderer followed by an editor that address all these issues:
import javax.swing.*; import javax.swing.table.*; import java.awt.*; import java.util.*; public class TextAreaRenderer extends JTextArea implements TableCellRenderer { private final DefaultTableCellRenderer adaptee = new DefaultTableCellRenderer(); /** map from table to map of rows to map of column heights */ private final Map cellSizes = new HashMap(); public TextAreaRenderer() { setLineWrap(true); setWrapStyleWord(true); } public Component getTableCellRendererComponent(// JTable table, Object obj, boolean isSelected, boolean hasFocus, int row, int column) { // set the colours, etc. using the standard for that platform adaptee.getTableCellRendererComponent(table, obj, isSelected, hasFocus, row, column); setForeground(adaptee.getForeground()); setBackground(adaptee.getBackground()); setBorder(adaptee.getBorder()); setFont(adaptee.getFont()); setText(adaptee.getText()); // This line was very important to get it working with JDK1.4 TableColumnModel columnModel = table.getColumnModel(); setSize(columnModel.getColumn(column).getWidth(), 100000); int height_wanted = (int) getPreferredSize().getHeight(); addSize(table, row, column, height_wanted); height_wanted = findTotalMaximumRowSize(table, row); if (height_wanted != table.getRowHeight(row)) { table.setRowHeight(row, height_wanted); } return this; } private void addSize(JTable table, int row, int column, int height) { Map rows = (Map) cellSizes.get(table); if (rows == null) { cellSizes.put(table, rows = new HashMap()); } Map rowheights = (Map) rows.get(new Integer(row)); if (rowheights == null) { rows.put(new Integer(row), rowheights = new HashMap()); } rowheights.put(new Integer(column), new Integer(height)); } /** * Look through all columns and get the renderer. If it is * also a TextAreaRenderer, we look at the maximum height in * its hash table for this row. */ private int findTotalMaximumRowSize(JTable table, int row) { int maximum_height = 0; Enumeration columns = table.getColumnModel().getColumns(); while (columns.hasMoreElements()) { TableColumn tc = (TableColumn) columns.nextElement(); TableCellRenderer cellRenderer = tc.getCellRenderer(); if (cellRenderer instanceof TextAreaRenderer) { TextAreaRenderer tar = (TextAreaRenderer) cellRenderer; maximum_height = Math.max(maximum_height, tar.findMaximumRowSize(table, row)); } } return maximum_height; } private int findMaximumRowSize(JTable table, int row) { Map rows = (Map) cellSizes.get(table); if (rows == null) return 0; Map rowheights = (Map) rows.get(new Integer(row)); if (rowheights == null) return 0; int maximum_height = 0; for (Iterator it = rowheights.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); int cellHeight = ((Integer) entry.getValue()).intValue(); maximum_height = Math.max(maximum_height, cellHeight); } return maximum_height; } }I know that the mechanism for handling the cell renderer for several columns is rather complex and inefficient. However, it caters for the general case where one instance of the cell renderer is used for several columns, or even for several tables.
You will notice that I am borrowing the formatting of the DefaultTableCellRenderer, so that the Text Area looks the same as the other cells, for the particular platform and JVM version.
As predicted in my newsletter three years ago, it was a "Kinderspiel" (child's play) to write the TextAreaEditor. A few points to note: I subclass the DefaultCellEditor, which only caters for JTextField, JComboBox and JCheckBox as editors. I therefore replace the editorComponent with my own, and also set my own delegate. In order to make it possible to edit more than the current cell allows, I put the TextArea for editing into a JScrollPane, and I set its border to null, to prevent it from showing the scrollbar unnecessarily.
import javax.swing.*; public class TextAreaEditor extends DefaultCellEditor { public TextAreaEditor() { super(new JTextField()); final JTextArea textArea = new JTextArea(); textArea.setWrapStyleWord(true); textArea.setLineWrap(true); JScrollPane scrollPane = new JScrollPane(textArea); scrollPane.setBorder(null); editorComponent = scrollPane; delegate = new DefaultCellEditor.EditorDelegate() { public void setValue(Object value) { textArea.setText((value != null) ? value.toString() : ""); } public Object getCellEditorValue() { return textArea.getText(); } }; } }I tested this on JDK 1.3.1 right up to JDK 1.5.0, under the Windows, Motif and Metal Look and Feels, and it appears the same as a normal JTable cell. Here is some test code:
import javax.swing.*; import javax.swing.table.*; public class TextAreaRendererTest extends JFrame { private final JTable table = new JTable(10, 4); public TextAreaRendererTest() { super(System.getProperty("java.vm.version")); // We use our cell renderer for columns 1, 2, 3 TableColumnModel cmodel = table.getColumnModel(); TextAreaRenderer textAreaRenderer = new TextAreaRenderer(); cmodel.getColumn(1).setCellRenderer(textAreaRenderer); cmodel.getColumn(2).setCellRenderer(new TextAreaRenderer()); // I am demonstrating that you can have several renderers in // one table, and they communicate with one another in // deciding the row height. cmodel.getColumn(3).setCellRenderer(textAreaRenderer); TextAreaEditor textEditor = new TextAreaEditor(); cmodel.getColumn(1).setCellEditor(textEditor); cmodel.getColumn(2).setCellEditor(textEditor); cmodel.getColumn(3).setCellEditor(textEditor); String test = "The lazy dog jumps over the quick brown fox"; for (int column = 0; column < 4; column++) { table.setValueAt(test, 0, column); table.setValueAt(test, 4, column); } test = test + test + test + test + test + test + test + test; table.setValueAt(test, 4, 2); getContentPane().add(new JScrollPane(table)); setSize(600, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new TextAreaRendererTest(); } }Well, it is late, and I need to hit the sack so that I can be fresh tomorrow for the Design Patterns Course at Alcatel Austria :)
Don't forget to let me know if you are currently in Austria!
created by Dr. Heinz M. Kabutz
0 komentar:
Posting Komentar