I have a simple problem which totally drives me crazy.
I have a JList, and would like its cells to expand depending on their content, which is text of variable length.
So I created a CustomCellRenderer like so:
#Override
public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean hasFocus)
{
final String text = (String) value;
final JTextArea ta = new JTextArea();
ta.setText(text);
ta.setFont(new Font("Dialog", Font.PLAIN, (int) Theme.FONTSIZE_TEXT));
ta.setForeground(Theme.FONTCOLOR_CONTENT);
ta.setLineWrap(true);
ta.setWrapStyleWord(true);
ta.setColumns(0);
ta.setBorder(BorderFactory.createEmptyBorder(10, Theme.PADDING, 0, 0));
return ta;
}
but the cells are only one text line high and the rest of the JTextArea is cut off. If I add
ta.setPreferredSize(new Dimension(0, 70));
I get a row height of 70 and I can see more of the JTextArea's text, but still not everything.
Is there any way to make JList expand its cells so that the whole content of the JTextArea is displayed?
there are maybe easiest and nicest way, I think that JTable with one TableColumn (and without TableHeader)in all cases better as JList, here is your Render MacOX version
then output should be
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.*;
//http://tips4java.wordpress.com/2008/10/26/text-utilities/
public class AutoWrapTest {
public JComponent makeUI() {
String[] columnNames = {" Text Area Cell Renderer "};
Object[][] data = {
{"123456789012345678901234567890"},
{"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddx"},
{"----------------------------------------------0"},
{">>>>>>>>>>>>>dddddddddddddddddddddddddddddddddddddddddddddddddd"
+ "dddddddxdddddddddddddddddddddddddddddddddddddddddddddd"
+ "dddddddddddx>>>>>>>>>>>>>>>>>>>>>>>>>|"},
{">>>>>>>>>>>>ddddddddddddddddddddddddddddddddddddddddddddddddddd"
+ "ddddddx>>>>>>>>>>>>>>>>>>>>>>>>>>|"},
{"a|"},
{">>>>>>>>bbbb>>>>>>>>>>>>>>>>>>>|"},
{">>>>>>>>>>>>>>>>>>|"},
{">>>>>>>>>>>>>dddddddddddddddddddddddddddddddddddddddddddddddddd"
+ "dddddddxdddddddddddddd123456789012345678901234567890dddddd"
+ "dddddddddddddddddddddddddddddddddddddx>>>>>>>>>>>>>>>>>>>>"
+ ">>>>>|"},
{">>>>>>>>>>>>>dddddddddddddd123456789012345678901234567890dddddd"
+ "dddddddddddddddddddddddddddddddddddddxdddddddddddddd123456"
+ "789012345678901234567890dddddddddddddddddddddddddddddddddd"
+ "ddddd123456789012345678901234567890ddddx>>>>>>>>>>>>>>>>>>"
+ ">>>>>>>|"},};
TableModel model = new DefaultTableModel(data, columnNames) {
private static final long serialVersionUID = 1L;
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
JTable table = new JTable(model) {
private static final long serialVersionUID = 1L;
#Override
public void doLayout() {
TableColumn col = getColumnModel().getColumn(0);
for (int row = 0; row < getRowCount(); row++) {
Component c = prepareRenderer(col.getCellRenderer(), row, 0);
if (c instanceof JTextArea) {
JTextArea a = (JTextArea) c;
int h = getPreferredHeight(a) + getIntercellSpacing().height;
if (getRowHeight(row) != h) {
setRowHeight(row, h);
}
}
}
super.doLayout();
}
private int getPreferredHeight(JTextComponent c) {
Insets insets = c.getInsets();
View view = c.getUI().getRootView(c).getView(0);
int preferredHeight = (int) view.getPreferredSpan(View.Y_AXIS);
return preferredHeight + insets.top + insets.bottom;
}
};
table.setEnabled(false);
table.setShowGrid(false);
table.setTableHeader(null);
table.getColumnModel().getColumn(0).setCellRenderer(new TextAreaCellRenderer());
//table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane sp = new JScrollPane(table);
sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
sp.setPreferredSize(new Dimension(250, 533));
JPanel p = new JPanel(new BorderLayout());
p.add(sp);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new AutoWrapTest().makeUI());
f.setLocation(100, 100);
f.pack();
f.setVisible(true);
}
}
class TextAreaCellRenderer extends JTextArea implements TableCellRenderer {
private static final long serialVersionUID = 1L;
private final Color evenColor = new Color(230, 240, 255);
public TextAreaCellRenderer() {
super();
setLineWrap(true);
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
setBackground((row % 2 == 0) ? evenColor : getBackground());
}
setFont(table.getFont());
setText((value == null) ? "" : value.toString());
return this;
}
}
Not sure if there is an elegant way.
One way that I know works is as follows :
In getListCellRendererComponent()
Use JLabel for the renderer
Convert the text in question to HTML and
use some logic to insert <br> in to the text where desired.
Then set the text as text for the JLabel component and return.
Try using JList's setFixedCellHeight() method.
Related
I have a rows of data, where I can make actions on a remote server.
I'm displaying these data in a JTable as it avoids the cost of creating a component for each row, and there's a lot.
I have managed to show the component and be able to interact with the cell, (however I still a first click).
But what I'm struggling with is that I want my editor component to be expandable, and of course update the current row height. And of course to revert to regular row height when collapsed. I think I need to register some listeners to the cell editor, but I'm currently not able to correctly do that.
ComponentListner::componentResized make the whole table being constantly repainted.
editorComponent.addPropertyChangeListener("preferredSize", propertyChangeListener), doesn't always repaint the table
editorComponent.addPropertyChangeListener("preferredSize", propertyChangeListener), doesn't always repaint the table
Hence this question.
Somewhat out of scope, I'm currently debating if the component should stay expanded when going to another cell (but this can be an expanded property that is part of the model).
Here's a minimal example without my attempts to register listeners on the editor component (in getTableCellEditorComponent).
Thanks in advance for ay pointers.
package io.github.bric3.fireplace.ui;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
public class DynamicCellRow {
#NotNull
private static JTable makeTable() {
var jTable = new JTable(
new Object[][]{
{"a", "charly"},
{"b", "tango"}
},
new Object[]{"id", "control"}
);
{
var richColumn = jTable.getColumnModel().getColumn(1);
richColumn.setCellRenderer(new TableCellRenderer() {
private final ExpandablePanel expandablePanelRenderComponent = new ExpandablePanel();
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
expandablePanelRenderComponent.setValue(value);
return updatePreferredRowHeight(table, expandablePanelRenderComponent, row, column);
}
});
richColumn.setCellEditor(new DynamicCellEditor());
}
return jTable;
}
public static <T extends JComponent> T updatePreferredRowHeight(JTable table, T cellComponent, int row, int column) {
// Adjust cell height per component
int originalPreferredHeight = cellComponent.getPreferredSize().height;
cellComponent.setSize(
table.getColumnModel().getColumn(column).getWidth(),
originalPreferredHeight
);
int newPreferredHeight = cellComponent.getPreferredSize().height;
if (table.getRowHeight(row) < newPreferredHeight) {
table.setRowHeight(row, newPreferredHeight);
}
return cellComponent;
}
static class ExpandablePanel extends JPanel {
private final JLabel comp;
ExpandablePanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// horizontal left-to-right layout
gbc.gridx = 0;
gbc.gridy = 0;
// resizing behavior
gbc.weightx = 1;
gbc.weighty = 1;
gbc.insets = new Insets(2, 2, 2, 2);
gbc.fill = GridBagConstraints.BOTH;
JPanel advanced = new JPanel();
{
advanced.setLayout(new BoxLayout(advanced, BoxLayout.Y_AXIS));
advanced.setBorder(new TitledBorder("Advance Settings"));
advanced.add(new JCheckBox("Live"));
advanced.add(new JCheckBox("Condition"));
advanced.add(new JCheckBox("Disable"));
}
advanced.setVisible(false);
var standard = new JPanel();
{
standard.setLayout(new BoxLayout(standard, BoxLayout.X_AXIS));
comp = new JLabel("Label 1");
standard.add(comp);
standard.add(new JButton("Button 1"));
var expandButton = new JButton("+");
expandButton.addActionListener(e -> {
if (advanced.isVisible()) {
advanced.setVisible(false);
expandButton.setText("+");
} else {
advanced.setVisible(true);
expandButton.setText("-");
}
});
standard.add(expandButton);
}
add(standard, gbc);
gbc.gridy++;
gbc.weighty = 0;
add(advanced, gbc);
}
public void setValue(Object value) {
comp.setText(value.toString());
}
}
private static class DynamicCellEditor extends AbstractCellEditor implements TableCellEditor {
Object value;
#Override
public Object getCellEditorValue() {
return value; // not changing
}
private final ExpandablePanel expandablePanel = new ExpandablePanel();
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
this.value = value;
expandablePanel.setValue(value);
return updatePreferredRowHeight(table, expandablePanel, row, column);
}
}
public static void main(String[] args) {
var contentPane = new JPanel(new BorderLayout());
contentPane.add(new JScrollPane(makeTable()));
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("DynamicCellRow");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(contentPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
#kleopatra Thank you! Your tip got me on the right track. I made the following code to precompute the row height during the layout operation.
And when a cell gets edited, the editor is added to the table, when then do a revalidation, and computes the height accordingly.
I had a problem before because setting the height in the renderer actually prevented the correct display of the cell component.
In the following code, notice how doLayout is adjusting the row height, according to cell component preferred size (wether the cell is rendered or edited).
Note I choose to collapse the component once editing is stopped, hence the listener in the DynamicExpndablePanelCellEditor.
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
public class DynamicCellRow {
public static void main(String[] args) {
var contentPane = new JPanel(new BorderLayout());
contentPane.add(new JScrollPane(makeTable()));
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("DynamicCellRow");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(contentPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
#NotNull
private static JTable makeTable() {
var jTable = new JTable(
new Object[][]{
{"a", "charly"},
{"b", "tango"}
},
new Object[]{"id", "control"}
) {
#Override
public void doLayout() {
super.doLayout();
adjustRowHeights();
}
private void adjustRowHeights() {
for (int row = 0; row < getRowCount(); row++) {
int rowHeight = getRowHeight();
for (int column = 0; column < getColumnCount(); column++) {
var editorComponent = getEditorComponent();
if (getEditingRow() == row && getEditingColumn() == column && editorComponent != null) {
editorComponent.setSize(getColumnModel().getColumn(column).getWidth(), 0);
rowHeight = Math.max(rowHeight, editorComponent.getPreferredSize().height);
} else {
var comp = prepareRenderer(getCellRenderer(row, column), row, column);
rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
}
}
setRowHeight(row, rowHeight);
}
}
};
{
var richColumn = jTable.getColumnModel().getColumn(1);
richColumn.setCellRenderer(new ExpandablePanelCellRenderer());
richColumn.setCellEditor(new DynamicExpndablePanelCellEditor());
}
return jTable;
}
static class ExpandablePanel extends JPanel {
private final JLabel comp;
private final JPanel advanced;
ExpandablePanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// horizontal left-to-right layout
gbc.gridx = 0;
gbc.gridy = 0;
// resizing behavior
gbc.weightx = 1;
gbc.weighty = 1;
gbc.insets = new Insets(2, 2, 2, 2);
gbc.fill = GridBagConstraints.BOTH;
advanced = new JPanel();
{
advanced.setLayout(new BoxLayout(advanced, BoxLayout.Y_AXIS));
advanced.setBorder(new TitledBorder("Advance Settings"));
advanced.add(new JCheckBox("Live"));
advanced.add(new JCheckBox("Condition"));
advanced.add(new JCheckBox("Disable"));
}
advanced.setVisible(false);
var standard = new JPanel();
{
standard.setLayout(new BoxLayout(standard, BoxLayout.X_AXIS));
comp = new JLabel("Label 1");
standard.add(comp);
standard.add(new JButton("Button 1"));
var expandButton = new JButton("+");
expandButton.addActionListener(e -> {
if (advanced.isVisible()) {
advanced.setVisible(false);
expandButton.setText("+");
} else {
advanced.setVisible(true);
expandButton.setText("-");
}
});
standard.add(expandButton);
}
add(standard, gbc);
gbc.gridy++;
gbc.weighty = 0;
add(advanced, gbc);
}
public void setValue(Object value) {
comp.setText(value.toString());
}
public void setAdvancedVisibility(boolean visible) {
advanced.setVisible(visible);
}
}
private static class DynamicExpndablePanelCellEditor extends AbstractCellEditor implements TableCellEditor {
Object value;
#Override
public Object getCellEditorValue() {
return value; // not changing
}
private final ExpandablePanel expandablePanel = new ExpandablePanel();
{
addCellEditorListener(new CellEditorListener() {
#Override
public void editingStopped(ChangeEvent e) {
expandablePanel.setAdvancedVisibility(false);
}
#Override
public void editingCanceled(ChangeEvent e) {
expandablePanel.setAdvancedVisibility(false);
}
});
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
this.value = value;
expandablePanel.setValue(value);
return expandablePanel;
}
}
private static class ExpandablePanelCellRenderer implements TableCellRenderer {
private final ExpandablePanel expandablePanelRenderComponent = new ExpandablePanel();
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
expandablePanelRenderComponent.setValue(value);
return expandablePanelRenderComponent;
}
}
}
This code changes the color of all the column, I need to change the single cell color at time. This code creates a table and installs a TableCellRenderer that modify the column color. It has two buttons that perform this action. The code speaks alone.
import java.awt.*;
import java.util.Random;
import javax.swing.*;
import javax.swing.table.*;
import java.awt.event.*;
public class Board extends JPanel {
private static final long serialVersionUID = 1L;
int boardHeight = 20;
int boardWidth = 10;
JTable table;
Random random = new Random();
public Board() {
setLayout(new BorderLayout()); // !!
DefaultTableModel model = new
DefaultTableModel(boardHeight, boardWidth) {
#Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
};
// !! table = new JTable(this.boardHeight, this.boardWidth);
table = new JTable(model);
for (int row = 0; row < model.getRowCount(); row++) {
for (int col = 0; col < model.getColumnCount(); col++) {
String s = random.nextBoolean() ? "red" : "yellow";
model.setValueAt(s, row, col);
}
}
//table.setDefaultRenderer(String.class, new BoardTableCellRenderer());
BoardTableCellRenderer mcr = new BoardTableCellRenderer();
table.setFocusable(true);
table.setShowGrid(false);
table.setRowMargin(0);
table.setIntercellSpacing(new Dimension(0, 0));
table.setRowSelectionAllowed(false);
table.setFillsViewportHeight(true);
table.setVisible(true);
//this.add(table);
this.setPreferredSize(new Dimension(table.getPreferredSize().width,
(table.getPreferredSize().height + 85)));
javax.swing.JButton b = new javax.swing.JButton("bottone");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//table.setDefaultRenderer(String.class, new BoardTableCellRenderer());
table.getColumnModel().getColumn(table.getSelectedColumn()).setCellRenderer(mcr);
table.revalidate(); table.repaint();
}
});
javax.swing.JButton b1 = new javax.swing.JButton("bottone default");
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
table.setDefaultRenderer(String.class, new DefaultTableCellRenderer());
table.revalidate(); table.repaint();
}
});
/*for (int columnIndex = 0; columnIndex < table.getColumnCount(); columnIndex ++) {
table.getColumnModel().getColumn(columnIndex).setCellRenderer(mcr);
}*/
//JScrollPane p1 = new JScrollPane();
//p1.add(table);
add(b, BorderLayout.SOUTH); // e.g. for the button
add(b1, BorderLayout.NORTH); // e.g. for the button
add(table, BorderLayout.CENTER); // e.g. for the button
}
private static void createAndShowUI() {
JFrame frame = new JFrame("Board");
frame.getContentPane().add(new Board());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class BoardTableCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected, boolean hasFocus, int row, int col) {
Component c = super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, col);
TableModel t = table.getModel();
Object valueAt = table.getModel().getValueAt(row, col);
String s = "";
if (valueAt != null) {
s = valueAt.toString();
}
//if (isSelected) {
// System.out.println("row: "+row+" "+"col: "+col);
if (col == 0 && row == 0) {
c.setForeground(Color.YELLOW);
c.setBackground(Color.gray);
}
//}
/* if (s.equalsIgnoreCase("yellow")) {
c.setForeground(Color.YELLOW);
c.setBackground(Color.gray);
} else {
c.setForeground(Color.black);
c.setBackground(Color.WHITE);
}
*/
return c;
}
}
This code changes the color of all the column,
The same renderer is used by all cells in the column.
So you need code something like:
if (!isSelected)
{
if (your special condition)
{
c.setBackground(...);
}
else // reset background to the default
{
c.setBackground(table.getBackground();
}
}
I have a simple JTable that shows the details (in column format) of a row from another JTable. This works nicely. However, sometimes the text in a row is very long so the user ends up having to scroll across which isnt neat.
How can I wrap the text in a row and allow the row height to change to show all the text in it.
Here is the code:
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
int selectedRow = table.getSelectedRow();
DefaultTableModel newModel = new DefaultTableModel();
String rowName = "Row: " + selectedRow;
newModel.setColumnIdentifiers(new Object[]{rowName});
for (int i = 0; i < table.getModel().getColumnCount(); i++) {
newModel.addRow(new Object[]{table.getModel().getValueAt(selectedRow, i)});
}
JTable newTable = new JTable(newModel) {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(140, 240);
}
};
newTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
newTable.setRowHeight(14, 30);
TableColumnAdjuster tcanewTable = new TableColumnAdjuster(newTable);
tcanewTable.setColumnHeaderIncluded(true);
tcanewTable.setColumnDataIncluded(true);
tcanewTable.setOnlyAdjustLarger( true );
tcanewTable.setDynamicAdjustment( true );
tcanewTable.adjustColumns();
// Apply any custom renderers and editors
JOptionPane.showMessageDialog(frame, new JScrollPane(newTable),
rowName, JOptionPane.PLAIN_MESSAGE);
}
}
});
You can accomplish this by using a JTextArea as a TableCellRenderer for that column in your table. For example:
static class WordWrapCellRenderer extends JTextArea implements TableCellRenderer {
WordWrapCellRenderer() {
setLineWrap(true);
setWrapStyleWord(true);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setText(value.toString());
setSize(table.getColumnModel().getColumn(column).getWidth(), getPreferredSize().height);
if (table.getRowHeight(row) != getPreferredSize().height) {
table.setRowHeight(row, getPreferredSize().height);
}
return this;
}
}
To use WordWrapCellRenderer in your table:
table.getColumnModel().getColumn(columnIndex).setCellRenderer(new WordWrapCellRenderer());
This is an update of the accepted answer.
It makes the renderer more efficient by limiting the number of getPreferredSize() calls.
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
/**
* The WordWrapRenderer can be used as a renderer for a JTable column. It will
* allow the text to wrap to a new line and adjust the table row height.
*
* Note. This renderer can only be used for a single column in the table.
*/
public class WordWrapRenderer extends JTextArea implements TableCellRenderer
{
WordWrapRenderer()
{
setLineWrap(true);
setWrapStyleWord(true);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
setText( (value == null) ? "" : value.toString() );
setSize(table.getColumnModel().getColumn(column).getWidth(), table.getRowHeight(row));
// Recalculate the preferred height now that the text and renderer width have been set.
int preferredHeight = getPreferredSize().height;
if (table.getRowHeight(row) != preferredHeight)
{
table.setRowHeight(row, preferredHeight);
}
return this;
}
private static void createAndShowGUI()
{
JTable table = new JTable(2, 3);
table.setValueAt("A", 0, 0);
table.setValueAt("This text should wrap by default", 0, 1);
table.getColumnModel().getColumn(1).setCellRenderer( new WordWrapRenderer() );
JFrame frame = new JFrame("TableBasic");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new JScrollPane( table ) );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
}
}
There are a lot of time, that I'm trying to make the Cell Renderer in the JavaOne 2007's talk maked by Shanon Hickey, Romain Guy and Chris Campbell, in the pages 38 and 39 (that you can find here : http://docs.huihoo.com/javaone/2007/desktop/TS-3548.pdf).
I asked Romain Guy, and Chris campbell, but without success, they say that they cannot pulish the source code.
So, any one can, give an idea, or even a source code on how to make this complex cell renderer ?
I learned this slides of many times, they say :
1)Cell Renderer: Column Spanning
Viewport clips column contents
and
2) Viewport moves to
expose the right text
for each column
I don't understand that, please can you give more in depth explanations ?
Cheers
Windows 7
JDK 1.7.0_21
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class ComplexCellRendererTest {
private static boolean DEBUG = true;
private JRadioButton r1 = new JRadioButton("scrollRectToVisible");
private JRadioButton r2 = new JRadioButton("setViewPosition");
public JComponent makeUI() {
String[] columnNames = {"AAA", "BBB"};
Object[][] data = {
{new Test("1", "aaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), "4"},
{new Test("2", "1234567890\nabcdefghijklmdopqrstuvwxyz"), "5"},
{new Test("3", "ccccccccccccccccccccccccccccccccccc\ndddddddddddd"), "6"},
};
DefaultTableModel model = new DefaultTableModel(data, columnNames) {
#Override public boolean isCellEditable(int row, int column) {
return false;
}
};
final JTable table = new JTable(model);
table.getTableHeader().setReorderingAllowed(false);
table.setRowSelectionAllowed(true);
table.setFillsViewportHeight(true);
table.setShowVerticalLines(false);
table.setIntercellSpacing(new Dimension(0,1));
table.setRowHeight(56);
for(int i=0; i<table.getColumnModel().getColumnCount(); i++) {
TableColumn c = table.getColumnModel().getColumn(i);
c.setCellRenderer(new ComplexCellRenderer());
c.setMinWidth(50);
}
ActionListener al = new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
DEBUG = (e.getSource()==r1);
table.repaint();
}
};
Box box = Box.createHorizontalBox();
ButtonGroup bg = new ButtonGroup();
box.add(r1); bg.add(r1); r1.addActionListener(al);
box.add(r2); bg.add(r2); r2.addActionListener(al);
r1.setSelected(true);
JPanel p = new JPanel(new BorderLayout());
p.add(box, BorderLayout.NORTH);
p.add(new JScrollPane(table));
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new ComplexCellRendererTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
static class ComplexCellRenderer extends JPanel implements TableCellRenderer {
private final JTextArea textArea = new JTextArea(2, 999999);
private final JLabel label = new JLabel();
private final JScrollPane scroll = new JScrollPane();
public ComplexCellRenderer() {
super(new BorderLayout(0,0));
scroll.setViewportView(textArea);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setBorder(BorderFactory.createEmptyBorder());
scroll.setViewportBorder(BorderFactory.createEmptyBorder());
scroll.setOpaque(false);
scroll.getViewport().setOpaque(false);
textArea.setBorder(BorderFactory.createEmptyBorder());
//textArea.setMargin(new Insets(0,0,0,0));
textArea.setForeground(Color.RED);
textArea.setOpaque(false);
label.setBorder(BorderFactory.createMatteBorder(0,0,1,1,Color.GRAY));
setBackground(Color.WHITE);
setOpaque(true);
add(label, BorderLayout.NORTH);
add(scroll);
}
#Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
Test test;
if(value instanceof Test) {
test = (Test)value;
} else {
String title = value.toString();
Test t = (Test)table.getModel().getValueAt(row, 0);
test = new Test(title, t.text);
}
label.setText(test.title);
textArea.setText(test.text);
Rectangle cr = table.getCellRect(row, column, false);
if(DEBUG) {
//Unexplained flickering on first row?
textArea.scrollRectToVisible(cr);
} else {
//Work fine for me (JDK 1.7.0_21, Windows 7 64bit):
scroll.getViewport().setViewPosition(cr.getLocation());
}
if(isSelected) {
setBackground(Color.ORANGE);
} else {
setBackground(Color.WHITE);
}
return this;
}
}
}
class Test {
public final String title;
public final String text;
//public final Icon icon;
public Test(String title, String text) {
this.title = title;
this.text = text;
}
}
I have a JTextArea that I'm using as a cell renderer for a table. In the getTableCellRenderercomponent method I have:
setText(getTextForCell());
setSize(table.getColumnModel().getColumn(column).getWidth(), 0);
getUI().getRootView(textArea).setSize(textArea.getWidth(), 0f);
updateSize();
private void updateSize() {
int prefHeight = textArea.getPreferredSize().height;
int currHeight = table.getRowHeight(r);
if (prefHeight > currHeight) {
table.setRowHeight(row, prefHeight);
}
When the text area uses wrap style word, it is sometimes a row short.
If I call this updateSize method from outside getTableCellRendererComponent then it works properly. But with a large table, calling update size on all rows whenever a column size adjusts is not feasible because it is too slow, so I've been trying to find a way to do the resize during the row rendering.
There is a related Java bug (that is marked as fixed but it does not appear that it really is) http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4446522, but this workaround does not appear to work when using word wrap.
Can anyone provide an alternative on how to make this work properly?
An alternative works properly
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.*;
public class AutoWrapInJTable {
public AutoWrapInJTable() {
String[] columnNames = {"TextAreaCellRenderer"};
Object[][] data = {
{"123456789012345678901234567890"},
{"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddx"},
{"----------------------------------------------0"},
{">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>|"},};
TableModel model = new DefaultTableModel(data, columnNames) {
private static final long serialVersionUID = 1L;
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
JTable table = new JTable(model) {
private static final long serialVersionUID = 1L;
#Override
public void doLayout() {
TableColumn col = getColumnModel().getColumn(0);
for (int row = 0; row < getRowCount(); row++) {
Component c = prepareRenderer(col.getCellRenderer(), row, 0);
if (c instanceof JTextArea) {
JTextArea a = (JTextArea) c;
int h = getPreferredHeight(a) + getIntercellSpacing().height;
if (getRowHeight(row) != h) {
setRowHeight(row, h);
}
}
}
super.doLayout();
}//http://tips4java.wordpress.com/2008/10/26/text-utilities/
private int getPreferredHeight(JTextComponent c) {
Insets insets = c.getInsets();
View view = c.getUI().getRootView(c).getView(0);
int preferredHeight = (int) view.getPreferredSpan(View.Y_AXIS);
return preferredHeight + insets.top + insets.bottom;
}
};
table.setEnabled(false);
table.setShowGrid(false);
table.setTableHeader(null);
table.getColumnModel().getColumn(0).setCellRenderer(new TextAreaCellRenderer());
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane sp = new JScrollPane(table);
sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
JPanel p = new JPanel(new BorderLayout());
p.add(sp);
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.add(p);
//f.pack();
f.setSize(200, 200);
f.setLocation(150, 150);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
AutoWrapInJTable autoWrapInJTable = new AutoWrapInJTable();
}
});
}
}
class TextAreaCellRenderer extends JTextArea implements TableCellRenderer {
private static final long serialVersionUID = 1L;
private final Color evenColor = new Color(230, 240, 255);
public TextAreaCellRenderer() {
super();
setLineWrap(true);
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
setBackground((row % 2 == 0) ? evenColor : getBackground());
}
setFont(table.getFont());
setText((value == null) ? "" : value.toString());
return this;
}
}