JTable in JScrollpane adjust height of the displayed component - java

I have a jtable within a jscrollpane. I use the jgoodies Form Layout and have put the scrollpane in a row that is set to "pref". This is how the table looks like right now:
I am using swingx JXTable by the way and have set the visibleRowCount to 2. But only half a row is displayed.
While making the SSCCE I realised that its not working properly because I have a custom TextAreaRenderer. I think because of this renderer, the row height is not being calculated properly. I am trying to figure out why, in the meantime if you guys can spot it that would be great...
public static void main(String[] args) {
JFrame frame=new JFrame();
JPanel panel=new FormDebugPanel();
frame.setContentPane(panel);
FormLayout layout=new FormLayout("1dlu:grow,200dlu:grow,1dlu:grow",
"10dlu,pref,5dlu,pref,10dlu");
panel.setLayout(layout);
String[] columns= {"1st Column","2nd Column","3rd Column"};
String[][] data= {
{"Sethu","Data","Something\nis\nhere"},
{"Sethu","Data","Something\nis\nhere"},
};
JXTable table=new JXTable(data,columns);
table.setVisibleRowCount(2);
TableColumnModel columnModel=table.getColumnModel();
columnModel.getColumn(2).setCellRenderer(new TextAreaRenderer());
JScrollPane scrlPan=new JScrollPane(table);
CellConstraints cc=new CellConstraints();
panel.add(scrlPan, cc.xy(2, 2));
panel.add(new JLabel("Label After Table"),cc.xy(2,4));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
And here's the renderer:
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<JTable,Map<Integer,Map<Integer,Integer>>> cellSizes = new HashMap<JTable,Map<Integer,Map<Integer,Integer>>>();
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<Integer,Map<Integer,Integer>> rows = cellSizes.get(table);
if (rows == null) {
cellSizes.put(table, rows = new HashMap<Integer,Map<Integer,Integer>>());
}
Map<Integer,Integer> rowheights = rows.get(new Integer(row));
if (rowheights == null) {
rows.put(new Integer(row), rowheights = new HashMap<Integer,Integer>());
}
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<TableColumn> 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<Integer,Map<Integer,Integer>> rows = cellSizes.get(table);
if (rows == null)
return 0;
Map<Integer,Integer> rowheights = rows.get(new Integer(row));
if (rowheights == null)
return 0;
int maximum_height = 0;
for (Iterator<Map.Entry<Integer, Integer>> it = rowheights.entrySet().iterator(); it.hasNext();) {
Map.Entry<Integer,Integer> entry = it.next();
int cellHeight = ((Integer) entry.getValue()).intValue();
maximum_height = Math.max(maximum_height, cellHeight);
}
return maximum_height;
}
}
When I use the TextAreaRenderer, then the setVisibleRowHeight() is not honoured correctly. I think it has something to do with not setting the row height properly in the renderer.

With JXTable, you can configure how many rows (or columns) to show initially:
table.setVisibleRowCount(2)
Here's a quick snippet showing how-to set the initial size in terms of rows and dynamically update the pref height in a TableModelListener
final JXTable table = new JXTable(3, 5);
table.setVisibleRowCount(table.getRowCount());
TableModelListener l = new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
if (!(e.getType() == TableModelEvent.INSERT)) return;
table.setVisibleRowCount(((TableModel) e.getSource()).getRowCount());
RootPaneContainer frame = (RootPaneContainer) SwingUtilities.windowForComponent(table);
((JComponent) frame.getContentPane()).revalidate();
}
};
table.getModel().addTableModelListener(l);
Action insert = new AbstractAction("add row") {
#Override
public void actionPerformed(ActionEvent e) {
((DefaultTableModel) table.getModel()).addRow(new Object[] {});
}
};
new Timer(500, insert).start();
JComponent comp = new JPanel(new MigLayout());
comp.add(new JScrollPane(table));
JXFrame frame = wrapInFrame(comp, "visibleRowCount");
show(frame, frame.getPreferredSize().width, frame.getPreferredSize().height * 4);

setPreferredScrollableViewportSize(Dimension)
is something you should look into. and here is a good tutorial : How to Use Scroll Panes

I believe you can use preferredSize, minSize and maxSize properties of the JSCrollPane to achieve this. If you set preferredSize to the minimum size you want and the maxsize to the maximum size you want, it should work.

Related

Change JList item colour when I want

I'm trying to build a JList in which some of the items have a different background colour than others but only when I want it.
I've been trying to do something like this up it produces errors.
static DefaultListModel<String> model = new DefaultListModel<>();
for(int K=0;K<Collectionsize();K++){
Jlist.setForeground(Color.red);
model.addElement(type 1);
for(int I=0;I<(subcollection.size();I++){
Jlist.setForeground(Color.white);
model.addElement(type 2);
}
}
There is no pattern between type 1 and 2 so I want change the colour when I want it rather than rely on if statements.
I see a lot of people talking about building custom render classes but I was aiming some something more simple.
Note: A list of items containing two pieces of information per item is better suited to being displayed in a table rather than a list, though you might adapt this to a list if needed. The same basic principle applies (use a rendering component).
This is what I mean:
Which is achieved by this rendering class:
class TrackCellRenderer extends DefaultTableCellRenderer {
HashMap<String, Color> colorMap = new HashMap<>();
Random r = new Random();
#Override
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected, boolean hasFocus,
int row, int column) {
Component c = super.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
JLabel l = (JLabel) c;
String s = (String) value;
if (column == 0) {
if (!colorMap.containsKey(s)) {
Color clr = new Color(
150 + r.nextInt(105),
150 + r.nextInt(105),
150 + r.nextInt(105));
colorMap.put(s, clr);
}
Color color = colorMap.get(s);
l.setBackground(color);
l.setOpaque(true);
} else {
l.setOpaque(false);
}
return l;
}
}
Note: it might be best to use an enum rather than randomly assigning a color, but with 3 albums and over a million possible colors, we should be pretty safe.
In this (two class) MCVE of just over 100 lines of code:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.table.*;
import java.util.*;
public class AlbumTrackTable {
private JComponent ui = null;
String[][] playList = {
{"The Will to Live", "Faded"},
{"The Will to Live", "Homeless Child"},
{"Oh Mercy", "Political Wrold"},
{"Oh Mercy", "What Was it You Wanted?"},
{"Red Sails in the Sunset", "Helps Me Helps You"},
{"Red Sails in the Sunset", "Redneck Wonderland"}
};
String[] columnNames = {"Album", "Track"};
AlbumTrackTable() {
initUI();
}
public void initUI() {
if (ui != null) {
return;
}
ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
DefaultTableModel trackModel = new DefaultTableModel(playList, columnNames);
JTable table = new JTable(trackModel);
ui.add(new JScrollPane(table));
TableCellRenderer renderer = new TrackCellRenderer();
table.setDefaultRenderer(Object.class, renderer);
table.setAutoCreateRowSorter(true);
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
AlbumTrackTable o = new AlbumTrackTable();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
class TrackCellRenderer extends DefaultTableCellRenderer {
HashMap<String, Color> colorMap = new HashMap<>();
Random r = new Random();
#Override
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected, boolean hasFocus,
int row, int column) {
Component c = super.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
JLabel l = (JLabel) c;
String s = (String) value;
if (column == 0) {
if (!colorMap.containsKey(s)) {
Color clr = new Color(
150 + r.nextInt(105),
150 + r.nextInt(105),
150 + r.nextInt(105));
colorMap.put(s, clr);
}
Color color = colorMap.get(s);
l.setBackground(color);
l.setOpaque(true);
} else {
l.setOpaque(false);
}
return l;
}
}

How to word wrap inside a JTable row

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() );
}
}

Error in removeRow in JButton [duplicate]

I want to delete all the rows of DefaultTable. I found two common ways to delete them on internet, but none of them works in my case because those methods does not exist in my DefaultTableModel. I wonder why. My code for using DefaultTableModel is
DefaultTableModel Table = (DefaultTableModel) Table.getModel();
One way to delete is
Table.removeRow(Table.getRowCount() - 1);
but this removerow method does not exist in my DefaultTableModel.
You can set the row count to 0.
setRowCount(0)
Quote from documentation:
public void setRowCount(int rowCount)
Sets the number of rows in the model. If the new size is greater than
the current size, new rows are added to the end of the model If the
new size is less than the current size, all rows at index rowCount and
greater are discarded.
But as you can't find removeRow either I suspect you haven't typed you model variable as DefaultTableModel perhaps, maybe just TableModel?
In that case cast your TableModel to DefaultTableModel like this:
DefaultTableModel model = (DefaultTableModel) table.getModel();
Why complicating simple things, but removes must be iterative,
if (myTableModel.getRowCount() > 0) {
for (int i = myTableModel.getRowCount() - 1; i > -1; i--) {
myTableModel.removeRow(i);
}
}
Code example
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.table.*;
public class RemoveAddRows extends JFrame {
private static final long serialVersionUID = 1L;
private Object[] columnNames = {"Type", "Company", "Shares", "Price"};
private Object[][] data = {
{"Buy", "IBM", new Integer(1000), new Double(80.50)},
{"Sell", "MicroSoft", new Integer(2000), new Double(6.25)},
{"Sell", "Apple", new Integer(3000), new Double(7.35)},
{"Buy", "Nortel", new Integer(4000), new Double(20.00)}
};
private JTable table;
private DefaultTableModel model;
public RemoveAddRows() {
model = new DefaultTableModel(data, columnNames) {
private static final long serialVersionUID = 1L;
#Override
public Class getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
};
table = new JTable(model) {
private static final long serialVersionUID = 1L;
#Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component c = super.prepareRenderer(renderer, row, column);
int firstRow = 0;
int lastRow = table.getRowCount() - 1;
int width = 0;
if (row == lastRow) {
((JComponent) c).setBackground(Color.red);
} else if (row == firstRow) {
((JComponent) c).setBackground(Color.blue);
} else {
((JComponent) c).setBackground(table.getBackground());
}
/*if (!isRowSelected(row)) {
String type = (String) getModel().getValueAt(row, 0);
c.setBackground("Buy".equals(type) ? Color.GREEN : Color.YELLOW);
}
if (isRowSelected(row) && isColumnSelected(column)) {
((JComponent) c).setBorder(new LineBorder(Color.red));
}*/
return c;
}
};
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
JButton button1 = new JButton("Remove all rows");
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if (model.getRowCount() > 0) {
for (int i = model.getRowCount() - 1; i > -1; i--) {
model.removeRow(i);
}
}
System.out.println("model.getRowCount() --->" + model.getRowCount());
}
});
JButton button2 = new JButton("Add new rows");
button2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Object[] data0 = {"Buy", "IBM", new Integer(1000), new Double(80.50)};
model.addRow(data0);
Object[] data1 = {"Sell", "MicroSoft", new Integer(2000), new Double(6.25)};
model.addRow(data1);
Object[] data2 = {"Sell", "Apple", new Integer(3000), new Double(7.35)};
model.addRow(data2);
Object[] data3 = {"Buy", "Nortel", new Integer(4000), new Double(20.00)};
model.addRow(data3);
System.out.println("model.getRowCount() --->" + model.getRowCount());
}
});
JPanel southPanel = new JPanel();
southPanel.add(button1);
southPanel.add(button2);
add(southPanel, BorderLayout.SOUTH);
}
public static void main(String[] args) {
RemoveAddRows frame = new RemoveAddRows();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Have you tried this
This works for me..
defaultTableModel.setRowCount(0);
Why don't you read the javadoc of DefaultTableModel?
public void removeRow(int row)
Removes the row at row from the model. Notification of the row being
removed will be sent to all the listeners.
public void setDataVector(Vector dataVector,
Vector columnIdentifiers)
Replaces the current dataVector instance variable with the new
Vector of rows, dataVector.
public void setRowCount(int rowCount)
Sets the number of rows in the model. If the new size is greater than
the current size, new rows are added to the end of the model If the
new size is less than the current size, all rows at index rowCount and
greater are discarded.
Simply keep removing the table model's first row until there are no more rows left.
// clean table
DefaultTableModel myTableModel = (DefaultTableModel) this.myjTable.getModel();
while (myTableModel.getRowCount() > 0) {
myTableModel.removeRow(0);
}
Ypu can write a method
public void clearTable()
{
getTableModel().setRowCount(0);
}
then call this method from the place that you need to clear the table

JTextArea as cell renderer does not seem to work with wrap style word

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;
}
}

Expand JList row height depending on content

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.

Categories