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;
}
}
Related
I would like to create a JTable in my application.
The table will contain two coumns, the first is the name of a software module.
The second column needs to contain a JComboBox with the revision numbers of the software module in the first column.
Is it even possible or I need to find another way to do this? (For example: put a button to the cell and choose the revision from a popup window)
Thank you in advance!
Here is one possible implementation to use a DefaultComboBoxModel<Integer> as the table column's data type:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
public class EditableComboCellEditorTest {
private JComponent makeUI() {
String[] columnNames = {"version", "revision"};
Object[][] data = {
{"1.7.0", makeModel(76, 79, 80)},
{"1.8.0", makeModel(91, 92, 101, 102)},
};
TableModel model = new DefaultTableModel(data, columnNames) {
#Override public Class<?> getColumnClass(int column) {
return column == 1 ? DefaultComboBoxModel.class : String.class;
}
};
JTable table = new JTable(model);
table.setRowHeight(32);
table.setAutoCreateRowSorter(true);
TableColumn col = table.getColumnModel().getColumn(1);
col.setCellRenderer(new ComboCellRenderer());
col.setCellEditor(new ComboCellEditor());
return new JScrollPane(table);
}
private static DefaultComboBoxModel<Integer> makeModel(Integer... items) {
return new DefaultComboBoxModel<Integer>(items) {
#Override public String toString() {
return Objects.toString(getSelectedItem(), "");
}
};
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new EditableComboCellEditorTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class ComboCellRenderer implements TableCellRenderer {
private final JPanel p = new JPanel(new GridBagLayout());
private final JComboBox<Integer> cb = new JComboBox<>();
#Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
p.removeAll();
p.setOpaque(false);
p.add(cb);
cb.setEditable(true);
cb.removeAllItems();
if (value instanceof DefaultComboBoxModel) {
DefaultComboBoxModel m = (DefaultComboBoxModel) value;
Object o = m.getSelectedItem();
if (o instanceof Integer) {
cb.addItem((Integer) o);
}
}
return p;
}
}
//https://java-swing-tips.blogspot.jp/2016/08/use-editable-jcombobox-as.html
class ComboCellEditor extends AbstractCellEditor implements TableCellEditor {
private final JPanel p = new JPanel(new GridBagLayout());
private final JComboBox<Integer> cb = new JComboBox<>();
protected ComboCellEditor() {
super();
cb.setEditable(true);
cb.addActionListener(e -> fireEditingStopped());
p.add(cb);
p.setOpaque(false);
}
#Override public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column) {
if (value instanceof ComboBoxModel) {
#SuppressWarnings("unchecked")
ComboBoxModel<Integer> m = (ComboBoxModel<Integer>) value;
cb.setModel(m);
}
return p;
}
#Override public Object getCellEditorValue() {
#SuppressWarnings("unchecked")
DefaultComboBoxModel<Integer> m = (DefaultComboBoxModel<Integer>) cb.getModel();
if (cb.isEditable()) {
Object o = cb.getEditor().getItem();
if (o instanceof Integer && m.getIndexOf((Integer) o) < 0) {
Integer value = (Integer) o;
int n = m.getSize();
Vector<Integer> list = new Vector<>(n + 1);
for (int i = 0; i < n; i++) {
list.add(m.getElementAt(i));
}
list.add(value);
Collections.sort(list);
m = new DefaultComboBoxModel<Integer>(list);
cb.setModel(m);
cb.setSelectedIndex(m.getIndexOf(value));
}
}
return m;
}
}
Override the getCellEditor(...) method to return the combo box:
import java.awt.*;
import java.util.List;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
public class TableComboBoxByRow extends JPanel
{
List<String[]> editorData = new ArrayList<String[]>(3);
public TableComboBoxByRow()
{
setLayout( new BorderLayout() );
// Create the editorData to be used for each row
editorData.add( new String[]{ "Red", "Blue", "Green" } );
editorData.add( new String[]{ "Circle", "Square", "Triangle" } );
editorData.add( new String[]{ "Apple", "Orange", "Banana" } );
// Create the table with default data
Object[][] data =
{
{"Color", "Red"},
{"Shape", "Square"},
{"Fruit", "Banana"},
{"Plain", "Text"}
};
String[] columnNames = {"Type","Value"};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
JTable table = new JTable(model)
{
// Determine editor to be used by row
public TableCellEditor getCellEditor(int row, int column)
{
int modelColumn = convertColumnIndexToModel( column );
if (modelColumn == 1 && row < 3)
{
JComboBox<String> comboBox1 = new JComboBox<String>( editorData.get(row));
return new DefaultCellEditor( comboBox1 );
}
else
return super.getCellEditor(row, column);
}
};
JScrollPane scrollPane = new JScrollPane( table );
add( scrollPane );
// table.getColumnModel().getColumn(1).setCellRenderer(new ComboBoxRenderer2() );
}
/*
class ComboBoxRenderer2 extends DefaultTableCellRenderer
{
#Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
label.setIcon(UIManager.getIcon("Table.descendingSortIcon"));
return label;
}
}
*/
private static void createAndShowUI()
{
JFrame frame = new JFrame("Table Combo Box by Row");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new TableComboBoxByRow() );
frame.setSize(200, 200);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
I have a JTable where one column displays values in the following format:
423545(50),[7568787(50)],53654656,2021947(50),[021947],2021947(50),[8021947(50)]
I am wondering if it is possible to display the values within square brackets in RED?
I have been googling around for the last few days and have found several examples showing how to set the 'background' of a cell but not really how to change the font of a cell especially not a specific part of the text.
public class myTableCellRenderer
extends DefaultTableCellRenderer {
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);
if (column == 3) {
c.setForeground(Color.YELLOW);
c.setBackground(Color.RED);
}
return c;
}
Is it really possible to change part of the text to be a different color (i.e. the text that is within the square brackets).
Edit
The text i showed as an example is the actual text shown in the table cell (the comma separators are not representing columns). The text displayed in the cell is a comma separated string that i display on the table in column 3.
As an example the table could look like this
product_id |product_name| invoice_numbers
12 | Books | 423545(50),[7568787(50)],53654656,2021947(50),[021947],2021947(50),[8021947(50)]
323 | Videos | 423545(50),[7568787(50)],53654656,2021947(50),[021947],2021947(50),[8021947(50)]
4434 | Music | 423545(50),[7568787(50)],53654656,2021947(50),[021947],2021947(50),[8021947(50)]
You must use a Cell renderer combined with HTML.
Here is a small demo example:
import java.awt.BorderLayout;
import java.awt.Component;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
public class TestTable2 {
class MyCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component tableCellRendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (value instanceof String) {
String string = (String) value;
if (string.indexOf('[') > -1) {
setText(getHTML(string));
}
}
return tableCellRendererComponent;
}
private String getHTML(String string) {
StringBuilder sb = new StringBuilder();
sb.append("<html>");
int index = 0;
while (index < string.length()) {
int next = string.indexOf('[', index);
if (next > -1) {
int end = string.indexOf(']', next);
if (end > -1) {
next++;
sb.append(string.substring(index, next));
sb.append("<span style=\"color: red;\">");
sb.append(string.substring(next, end));
sb.append("</span>");
index = end;
} else {
break;
}
} else {
break;
}
}
sb.append(string.substring(index, string.length()));
sb.append("</html>");
return sb.toString();
}
}
protected void initUI() {
DefaultTableModel model = new DefaultTableModel();
for (int i = 0; i < 2; i++) {
model.addColumn("Col-" + (i + 1));
}
for (int i = 0; i < 200; i++) {
Vector<Object> row = new Vector<Object>();
for (int j = 0; j < 5; j++) {
row.add("423545(50),[7568787(50)],53654656,2021947(50),[021947],2021947(50),[8021947(50)]");
}
model.addRow(row);
}
JTable table = new JTable(model);
table.setDefaultRenderer(Object.class, new MyCellRenderer());
JFrame frame = new JFrame(TestTable2.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollpane = new JScrollPane(table);
frame.add(scrollpane, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestTable2().initUI();
}
});
}
}
And the result:
This is what you are looking for Cell render
How to proceed:
get the default cell render component using getTableCellRendererComponent() function with the appropriate parameters.
parse the text of the cell and apply your formatting using setForeground() function.
Is it really possible to change part of the text to be a different color
yes is possible, for
simple highlighter is possible with JTextField/JTextArea as Renderers component
multiple of the Highlighter have to look for JTextPane as Renderers component
(easier of ways) you can to formatting cell by using Html (todays Java up to Html3.2)
Yes It is possible.
EDIT
First you need to create a subclass of DefaultTableCellRenderer where you override getTableCellRendererComponent method to render the desired column according to your need.
And then change the renderer for that column by the subclass of DefaultTableCellRenderer.
Here is the example to achieve this task:
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import java.util.*;
public class TableExample extends JFrame
{
JTable myTable ;
Object[][] data= {
{"34","[56],987,[(56)]"},
{"5098","345,([{78}])"},
{"567","4312"}
};
Object[] col = {"First","Second"};
public TableExample()
{
super("CellRendererExample");
}
public void prepareAndShowGUI()
{
myTable = new JTable(data,col);
myTable.getColumnModel().getColumn(1).setCellRenderer(new MyTableCellRenderer());
JScrollPane jsp = new JScrollPane(myTable);
getContentPane().add(jsp);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public class MyTableCellRenderer extends DefaultTableCellRenderer
{
public Component getTableCellRendererComponent(JTable table,Object oValue, boolean isSelected, boolean hasFocus, int row, int column)
{
Component c = super.getTableCellRendererComponent(table, oValue,isSelected, hasFocus,row, column);
String value = (String)oValue;
StringBuilder sBuilder = new StringBuilder();
sBuilder.append("<HTML><BODY>");
StringTokenizer tokenizer = new StringTokenizer(value,",");
while (tokenizer.hasMoreTokens())
{
String token = tokenizer.nextToken();
int index = token.indexOf("[");
if (index != -1)
{
sBuilder.append(token.substring(0,index));
int lastIndex = token.lastIndexOf(']');
String subValue = token.substring(index + 1,lastIndex);
sBuilder.append("[<FONT color = red>"+subValue+"</FONT>]");
if (lastIndex < token.length() -1)
{
sBuilder.append(token.substring(lastIndex+1,token.length()));
}
sBuilder.append(",");
}
else
{
sBuilder.append(token+",");
}
}
if (sBuilder.lastIndexOf(",") == sBuilder.length() - 1)
{
sBuilder.deleteCharAt(sBuilder.length() - 1 );
}
sBuilder.append("</BODY></HTML>");
value = sBuilder.toString(); ;
JLabel label =(JLabel)c;
label.setText(value);
return label;
}
}
public static void main(String st[])
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
TableExample tae = new TableExample();
tae.prepareAndShowGUI();
}
});
}
}
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;
}
}
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.
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.