JTable changing cells colour's from a given ArrayList - java

So I have the following class which defines basic parameters of an error
public class Error {
public String desc;
public int rowNumber;
public int colNumber;
public int fixNumber;
public Error(String desc,int row, int col, int fix){
this.desc = desc;
rowNumber = row++;
colNumber = col++;
fixNumber = fix;
}
...
My gui Class
public class Gui extends JFrame {
AbstractTableModel model;
JTable table;
public void start(AbstractTableModel model) {
this.model = model;
table=new JTable(model){
#Override
public boolean isCellEditable(int arg0, int arg1) {
return false;
}
};
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
TableColumn column = null;
for (int i = 0; i < model.getColumnCount(); i++) {
column = table.getColumnModel().getColumn(i);
column.setPreferredWidth(120);
column.setMaxWidth(300);
column.setMinWidth(50);
column.setCellRenderer(new customCellRender());
}
JScrollPane pane = new JScrollPane(table);
pane.setPreferredSize(new Dimension(900,900));
add(pane);
setLayout(new FlowLayout());
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
}
I have attempted to make a cellRenderer but at the moment it does not have the intended effect. Because it colors all the cells.
#SuppressWarnings("serial")
public class customCellRender extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column){
Component c = null;
c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
c.setBackground(new java.awt.Color(255, 72, 72));
return c;
}
I then have a List which stores instances of the error class.What I cant figure out is how to ONLY change the color of the cells in my table based on the Error(rowNumber,colNumber). Considering that the errors are in a list structure, so I would have to iterate over and somehow pass each error column and row to the renderer. Is that possible ?

Your code colors all the cells because the component is reused, not recreated every time. Just add a
if (error) {
c.setBackground(errorBackground);
} else {
c.setBackground(table.getBackground);
}
A couple of points to make:
Don't create a new Color every time. It's expensive and you're using the same one anyway.
Use a Set for your errors to make contains quick, otherwise your rendering may become very slow with a large number of those.

Related

Cannot display JComboBox in JTable with TableModel

Below code to display a JTable with 3 columns, which respectively contain a JComboBox, a String and a double, and which should display yellow. The problem is that I cannot get the JComboBox in the first column to display as ... a combo box; instead I get a String saying "javax.swing.JComboBox...". What am I doing wrong?
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
import java.awt.*;
public class BadDialog extends JDialog {
//Instantiate the data for the table, which is 2 rows x 3 cols
private final JComboBox col0ComboBox = new JComboBox(new String[]{"aaa", "bbb"}); //Goes in all rows of Col 0
private final String[] col1Data = {"Mickey", "Mouse"};
private final double[] col2Data = {111, 222};
public BadDialog() {
//Instantiate table
JTable badTable = new JTable();
//Assign a tableModel to the table, put the table in a scroller, add it to this dialog, and sort out the renderer
TableModel badTableModel = new BadTableModel();
badTable.setModel(badTableModel);
JScrollPane scroller = new JScrollPane(badTable);
add(scroller);
BadTableCellRenderer badTableCellRenderer = new BadTableCellRenderer();
badTable.setDefaultRenderer(JComboBox.class, badTableCellRenderer); //Col 0
badTable.setDefaultRenderer(String.class, badTableCellRenderer); //Col 1
badTable.setDefaultRenderer(Double.class, badTableCellRenderer); //Col 2
//Assign col0ComboBox to Col 0
badTable.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(col0ComboBox));
//Show the dialog
setPreferredSize(new Dimension(300, 470));
pack();
setModal(true);
setLocation(10, 10);
setVisible(true);
}
private final class BadTableModel extends AbstractTableModel {
#Override
public int getRowCount() {
return 2;
}
#Override
public int getColumnCount() {
return 3;
}
#Override
public Object getValueAt(int rowIndex, int colIndex) {
if (colIndex == 0) return col0ComboBox;
if (colIndex == 1) return col1Data[rowIndex];
return col2Data[rowIndex];
}
#Override
public Class<?> getColumnClass(int colIndex) {
if (colIndex == 0) return JComboBox.class;
if (colIndex == 1) return String.class;
return Double.class;
}
}
private static class BadTableCellRenderer extends DefaultTableCellRenderer {
#Override
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);
//Make all columns yellow
c.setBackground(Color.YELLOW);
c.setForeground(Color.RED);
c.setFont(new Font("Dialog", Font.PLAIN, 12));
return c;
}
}
public static void main(String[] args) {
new BadDialog();
}
}
Never return a component in a TableModel. The whole point of having a separate model and view is that the model contains only data, not components. The model’s job is to provide data; the view’s job is to determine how to display that data.
Your TableModel’s getColumnClass method should look like this:
public Class<?> getColumnClass(int colIndex) {
if (colIndex == 0) return String.class; // String, not JComboBox
if (colIndex == 1) return String.class;
return Double.class;
}
and your getValueAt method needs to return the actual data value at that row:
public Object getValueAt(int rowIndex, int colIndex) {
if (colIndex == 0) return (rowIndex % 1 == 0 ? "aaa" : "bbb");
if (colIndex == 1) return col1Data[rowIndex];
return col2Data[rowIndex];
}
The cell renderer is part of the view, not the model, so it can make use of a JComboBox. Your render needs to use the value argument to modify your JComboBox:
private static class BadTableCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
if (row != 0) {
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
}
JComboBox c = col0ComboBox;
c.setSelectedItem(value);
//Make all columns yellow
c.setBackground(Color.YELLOW);
c.setForeground(Color.RED);
c.setFont(new Font("Dialog", Font.PLAIN, 12));
return c;
}
}
Your TableModel is completely wrong.
The data of the TableModel must be stored in the TableModel, not as an Array external to the model.
Don't extend AbstractTableModel. Instead extend DefaultTableModel. The DefaultTableModel already provides data storage and methods to update the data of each cell. Then the only method you need to override is the getColumnClass(...) method, to make sure the appropriate renderer/editor is used for each column.
See: Sorting JTable programmatically for a basic example showing how to add initial data to a JTable. The getColumnClass(...) method in that example is more generic.
You can simplify the getColumnClass(...) method as follows:
#Override
public Class getColumnClass(int column)
{
switch (column)
{
case 2: return Double.class;
default: return super.getColumnClass(column);
}
}
Then if you want the editor of the first column to be a combobox you would use:
badTable.getColumnModel().getColumn(0).setCellEditor( new DefaultCellEditor(col0ComboBox));
You should NOT use the same renderer for all 3 columns because typically numbers are displayed right aligned in the column.
There should be no need for custom renderers. Instead you change properties of the table:
table.setBackground(...);
table.setForeground(...);
table.setFont(...);
These values will then be used by each of the default column renderers.

How to change the color of JTable row after clicking a button

I want to change the color of JTable row after clicking a button. I found many examples that show how to initiate a table with different colors of rows, and also how to change the color on selecting a row. However, I wonder what is the right way to change the color on clicking a JButton.
TableCellRenderer colorRenderer = new ColorRenderer();
table.setDefaultRenderer(String.class, colorRenderer);
private class ColorRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
#Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (hasFocus) {
setBackground(Color.cyan);
} else if (isSelected) {
setBackground(table.getSelectionBackground());
} else {
setBackground(table.getBackground());
}
return this;
}
}
Considering that you are loading data in JTable as :
public void fillTable(){
List<String> columns = new ArrayList<String>();
List<String[]> values = new ArrayList<String[]>();
columns.add("col1");
columns.add("col2");
columns.add("col3");
for (int i = 0; i < 100; i++) {
values.add(new String[] {"val"+i+" col1","val"+i+" col2","val"+i+" col3"});
}
TableModel tableModel = new DefaultTableModel(values.toArray(new Object[][] {}), columns.toArray());
tableName.setModel(tableModel);
}
Then, you can use like this class to set a rendered to JTable :
public class EvenOddRenderer implements TableCellRenderer {
public static final DefaultTableCellRenderer DEFAULT_RENDERER = new DefaultTableCellRenderer();
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
DEFAULT_RENDERER.setHorizontalAlignment(JLabel.CENTER);
Component renderer = DEFAULT_RENDERER.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
((JLabel) renderer).setOpaque(true);
Color foreground, background;
Color alternate = new Color(0xC0, 0xC0, 0xF0);
Color lightBlue = new Color(204, 204, 255);
if (isSelected) {
foreground = Color.black;
background = Color.gray;
} else {
if (row % 2 == 0) {
foreground = Color.black;
background = Color.white;
} else {
foreground = Color.black;
background = lightBlue;
}
}
renderer.setForeground(foreground);
renderer.setBackground(background);
return renderer;
}
}
Like this :
TableCellRenderer renderer = new EvenOddRenderer();
tableName.setDefaultRenderer(Object.class, renderer);
tableName.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
Then you can use setRowSelectionInterval() method to select any row :
private void btn(java.awt.event.ActionEvent evt) {
int index = 0;
tableName.setRowSelectionInterval(index, index);
}
Here you are selecting the first row in JTable
In this example, you are choosing Color.gray as JTable selection color
You can change it for sure
Check out Table Row Rendering.
This approach will also work for tables that contain different data types in each column so you don't have to create custom renderers for every type of data.

Add margin to JTable column (Mac)

I have a JTable looking like this:
Is it possible to add a margin for the column text?
I have tried to add a TableCellRenderer and add an EmptyBorder. Problem: the background color gets white like this:
Any idea how to add margin and keep the default background color?
public class JTableColumnMargin extends JFrame {
public JTableColumnMargin() {
Object rowData[][] = { { "Row1-Column1", "Row1-Column2", "Row1-Column3" }, { "Row2-Column1", "Row2-Column2", "Row2-Column3" } };
Object columnNames[] = { "Column One", "Column Two", "Column Three" };
JTable table = new JTable(rowData, columnNames);
for (int i = 0; i < table.getColumnCount(); i++) {
table.getTableHeader().getColumnModel().getColumn(i).setHeaderRenderer(new HeaderRenderer(table));
}
JScrollPane scrollPane = new JScrollPane(table);
this.add(scrollPane, BorderLayout.CENTER);
this.setSize(500, 150);
this.setVisible(true);
}
private static class HeaderRenderer implements TableCellRenderer {
private DefaultTableCellRenderer renderer;
private HeaderRenderer(JTable table) {
renderer = (DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer();
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
Component c = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
JLabel label = (JLabel) c;
label.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
return label;
}
}
public static void main(String[] args) {
new JTableColumnMargin();
}
}
Delegating to the original renderer basically is the right direction to go, just slightly differently:
use a compoundBorder of the original and the emptyBorder
nudge the original renderer to re-set the border to its original by making the padded border a UIResource
Something like:
#Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
JComponent comp = (JComponent) originalRenderer.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
Border originalBorder = comp.getBorder();
comp.setBorder(new CompoundBorderUIResource(originalBorder,
BorderFactory.createEmptyBorder(0, 20, 0, 0)));
return comp;
}
It's brittle, though, as LAFs might ignore your setting: the trick works fine for WinLAF, but not at all for Nimbus - so don't know the outcome on Mac.
Try to use next header Renderer:
private static class HeaderRenderer extends JLabel implements TableCellRenderer {
private HeaderRenderer() {
setBorder(BorderFactory.createCompoundBorder(UIManager.getBorder("TableHeader.cellBorder"),BorderFactory.createEmptyBorder(0, 10, 0, 10)));
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
setText(value.toString());
return this;
}
}

JTable Right Align Header

Basically, I have a JTable containing columns with right-aligned cells but left-aligned headers which looks really bad. I would like to right-align the headers of these columns without altering the "Look and Feel" of the headers.
Thanks
Here's an alternate approach to modifying the TableCellRenderer of a table's JTableHeader. It's not strictly necessary for this usage, but it minimizes the impact on the UI delegate's appearance.
Typical usage:
JTable table = new JTable(…);
JTableHeader header = table.getTableHeader();
header.setDefaultRenderer(new HeaderRenderer(table));
Custom header renderer:
private static class HeaderRenderer implements TableCellRenderer {
DefaultTableCellRenderer renderer;
public HeaderRenderer(JTable table) {
renderer = (DefaultTableCellRenderer)
table.getTableHeader().getDefaultRenderer();
renderer.setHorizontalAlignment(JLabel.CENTER);
}
#Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int col) {
return renderer.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, col);
}
}
Try this:
((DefaultTableCellRenderer)table.getTableHeader().getDefaultRenderer())
.setHorizontalAlignment(JLabel.RIGHT);
DefaultTableCellRenderer renderer = (DefaultTableCellRenderer) your_jtable.getTableHeader().getDefaultRenderer();
renderer.setHorizontalAlignment(0);
Where 0 is Centre.
The HeaderRenderer shown above (2011/sep/21 by trashgod) combined with code from Heisenbug (2011/sep/21) , will only work correctly if you have all headers aligned the same.
If you want to align different headers differently, then you will have to use the following code:
int[] alignments = new int[] { JLabel.LEFT, JLabel.RIGHT, JLabel.RIGHT };
for (int i = 0 ; i < jTable.getColumnCount(); i++){
jTable.getTableHeader().getColumnModel().getColumn(i)
.setHeaderRenderer(new HeaderRenderer(jTable, alignments[i]));
}
and
private static class HeaderRenderer implements TableCellRenderer {
DefaultTableCellRenderer renderer;
int horAlignment;
public HeaderRenderer(JTable table, int horizontalAlignment) {
horAlignment = horizontalAlignment;
renderer = (DefaultTableCellRenderer)table.getTableHeader()
.getDefaultRenderer();
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int col) {
Component c = renderer.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, col);
JLabel label = (JLabel)c;
label.setHorizontalAlignment(horAlignment);
return label;
}
}
That is:
Set the alignment in getTableCellRendererComponent , and not in the HeaderRenderer constructor.
A thing to remember about wrapping default table headers: do not hold on to a reference of them.
If you (or your users) are using a Windows Classic theme on Windows 7 and your application sets default system LAF, the answer posted by #trashgod may cause problems for you.
It is affected by this bug, posted a decade ago (seriously). If your table is showing and you switch the theme in Windows preferences from an Aero Theme to Windows Classic, there will be a barrage of NPEs. You are NOT supposed to hold on to a reference of a renderer as it may become invalid at some point in time. The wrapping should be done in a dynamic way, as suggested in the comments of the bug report. I took the code from there and created the following runnable example:
import java.awt.*;
import java.lang.ref.WeakReference;
import javax.swing.*;
import javax.swing.table.*;
public class TestFrame extends JFrame {
private static final boolean I_WANT_THE_BUG_TO_HAPPEN = true;
public static void main(String[] args) throws IllegalAccessException, ClassNotFoundException, InstantiationException, UnsupportedLookAndFeelException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
int res = JOptionPane.showConfirmDialog(null, "Do you want to use the XP L&F?", "laffo", JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.YES_OPTION) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (Exception ex) {
}
}
new TestFrame().setVisible(true);
}
});
}
public class MyModel extends AbstractTableModel {
public int getRowCount() {
return 10;
}
public int getColumnCount() {
return 10;
}
public Object getValueAt(int rowIndex, int columnIndex) {
return "" + rowIndex + " X " + columnIndex;
}
}
public class MyJTable extends JTable {
/**
*
*/
private static final long serialVersionUID = -233098459210523146L;
public MyJTable(TableModel model) {
super(model);
}
public void doSomething() {
System.out.println("HEHE");
}
}
public class MyAlternativeJTable extends JTable {
private WeakReference<TableCellRenderer> wrappedHeaderRendererRef = null;
private TableCellRenderer wrapperHeaderRenderer = null;
public MyAlternativeJTable(TableModel model) {
super(model);
}
private class MyAlternativeTableColumn extends TableColumn {
MyAlternativeTableColumn(int modelIndex) {
super(modelIndex);
}
#Override
public TableCellRenderer getHeaderRenderer() {
TableCellRenderer defaultHeaderRenderer
= MyAlternativeJTable.this.getTableHeader().getDefaultRenderer();
if (wrappedHeaderRendererRef == null
|| wrappedHeaderRendererRef.get() != defaultHeaderRenderer) {
wrappedHeaderRendererRef
= new WeakReference<TableCellRenderer>(defaultHeaderRenderer);
wrapperHeaderRenderer
= new DecoratedHeaderRenderer(defaultHeaderRenderer);
}
return wrapperHeaderRenderer;
}
}
#Override
public void createDefaultColumnsFromModel() {
TableModel m = getModel();
if (m != null) {
// Remove any current columns
TableColumnModel cm = getColumnModel();
while (cm.getColumnCount() > 0) {
cm.removeColumn(cm.getColumn(0));
}
// Create new columns from the data model info
for (int i = 0; i < m.getColumnCount(); i++) {
TableColumn newColumn = new MyAlternativeTableColumn(i);
addColumn(newColumn);
}
}
}
}
private JPanel jContentPane = null;
private JScrollPane jScrollPane = null;
private JTable table1 = null;
private JScrollPane jScrollPane1 = null;
private JTable table2 = null;
/**
* This is the default constructor
*/
public TestFrame() {
super();
initialize();
int res = JOptionPane.showConfirmDialog(null, "Do you want to call updateUI() on the tables ?", "laffo", JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.YES_OPTION) {
table2.updateUI();
table1.updateUI();
}
}
/**
* This method initializes this
*
* #return void
*/
private void initialize() {
this.setSize(753, 658);
this.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
this.setContentPane(getJContentPane());
this.setTitle("JFrame");
}
/**
* This method initializes jContentPane
*
* #return javax.swing.JPanel
*/
private JPanel getJContentPane() {
if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(null);
jContentPane.add(getJScrollPane(), null);
jContentPane.add(getJScrollPane1(), null);
}
return jContentPane;
}
/**
* This method initializes jScrollPane
*
* #return javax.swing.JScrollPane
*/
private JScrollPane getJScrollPane() {
if (jScrollPane == null) {
jScrollPane = new JScrollPane();
jScrollPane.setBounds(new java.awt.Rectangle(358, 0, 387, 618));
jScrollPane.setViewportView(getTable1());
}
return jScrollPane;
}
/**
* This method initializes table1
*
* #return javax.swing.JTable
*/
private JTable getTable1() {
if (table1 == null) {
table1 = new JTable(new MyModel());
}
return table1;
}
/**
* This method initializes jScrollPane1
*
* #return javax.swing.JScrollPane
*/
private JScrollPane getJScrollPane1() {
if (jScrollPane1 == null) {
jScrollPane1 = new JScrollPane();
jScrollPane1.setBounds(new java.awt.Rectangle(0, 0, 350, 618));
jScrollPane1.setViewportView(getTable2());
}
return jScrollPane1;
}
/**
* This method initializes table2
*
* #return javax.swing.JTable
*/
private JTable getTable2() {
if (table2 == null) {
if (I_WANT_THE_BUG_TO_HAPPEN) {
table2 = new MyJTable(new MyModel());
JTableHeader header = table2.getTableHeader();
TableCellRenderer render = new DecoratedHeaderRenderer(header.getDefaultRenderer());
header.setDefaultRenderer(render);
} else {
table2 = new MyAlternativeJTable(new MyModel());
}
}
return table2;
}
private class DecoratedHeaderRenderer implements TableCellRenderer {
public DecoratedHeaderRenderer(TableCellRenderer render) {
this.render = render;
}
private TableCellRenderer render;
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component c = render.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
return c;
}
}
}
Simply run the example and choose Yes twice and watch it break apart. Then change the I_WANT_THE_BUG_TO_HAPPEN static member to false and repeat. The case with this member set to true is essentially the same as the most upvoted answer here. The most important part of this example is the extended JTable (MyAlternativeJTable) which does the wrapping dynamically.
The currently accepted answer to this question is widely used, but it is ill advised. You can reproduce it with lost of applications, including Netbeans 8.0.2 (which itself is Java based) while it is showing a sortable table, such as Window > IDE Tools > Notifications, where you'll also get the NPE reports, ironically. Just switch the Windows theme from Aero to Windows Classic (via right-click Desktop > Personalize > Change the visuals and sounds on your computer) on Windows 7.
If you are using Glazed Lists and call ca.odell.glazedlists.swing.TableComparatorChooser.install, you are also affected. It injects its own custom renderer for sorting arrows.
I stumbled upon this by coincidence while trying to find a solution for this question which I suspect is related.
for (int i = 0 ; i < table.getColumnCount(); i++){
DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
renderer.setHorizontalAlignment(SwingConstants.RIGHT);
table.getColumn(i).setHeaderRenderer(renderer);
}
DefaultTableCellRenderer renderer = (DefaultTableCellRenderer)
MSISDNTable.getTableHeader().getDefaultRenderer();
renderer.setHorizontalAlignment(JLabel.RIGHT);
where MSISDNTable is your table
DefaultTableCellRenderer defaultHeaderRenderer = (DefaultTableCellRenderer) getTableHeader().getDefaultRenderer();
defaultHeaderRenderer.setHorizontalAlignment(JLabel.CENTER);
getTableHeader().setDefaultRenderer(defaultHeaderRenderer);
I have tested in JAVA8. working fine.
Try this code,
JTableHeader jtableHeader = jtable.getTableHeader();
DefaultTableCellRenderer rend = (DefaultTableCellRenderer) jtable.getTableHeader().getDefaultRenderer();
rend.setHorizontalAlignment(JLabel.CENTER);
jtableHeader.setDefaultRenderer(rend);
I have created a class based on the solution of pvbemmelen62, that can be used very easily, for example:
AlignHeaderRenderer.install(myTable, new int[] { SwingConstants.RIGHT,
SwingConstants.RIGHT, SwingConstants.LEFT });
or
AlignHeaderRenderer.install(myTable, 0, SwingConstants.RIGHT);
AlignHeaderRenderer.install(myTable, 1, SwingConstants.RIGHT);
Here's the code:
public class AlignHeaderRenderer implements TableCellRenderer {
private final TableCellRenderer renderer;
private final int alignment;
public static void install(final JTable table, final int[] alignments) {
for (int i = 0; i < alignments.length; ++i)
install(table, i, alignments[i]);
}
public static void install(final JTable table, final int row,
final int alignment) {
table.getTableHeader().getColumnModel().getColumn(row)
.setHeaderRenderer(new AlignHeaderRenderer(table, alignment));
}
private AlignHeaderRenderer(final JTable table, final int alignment) {
renderer = table.getTableHeader().getDefaultRenderer();
this.alignment = alignment;
}
#Override
public Component getTableCellRendererComponent(final JTable table,
final Object value, final boolean isSelected,
final boolean hasFocus, final int row, final int col) {
final Component c = renderer.getTableCellRendererComponent(table,
value, isSelected, hasFocus, row, col);
((JLabel) c).setHorizontalAlignment(alignment);
return c;
}
}
The secret is to use the renderer from a dummy table to get correct L&F, and copy the alignment from the real table's row renderer. That way each column in aligned separately. Here is the code:
table.getTableHeader().setDefaultRenderer(new DefaultTableCellRenderer() {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
Component c2 = dummy.getTableHeader().getDefaultRenderer().getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
if (table.getRowCount() > 0) {
Component c3 = table.getCellRenderer(0, col).getTableCellRendererComponent(table, value, isSelected, hasFocus, 0, col);
if (c2 instanceof JLabel && c3 instanceof JLabel)
((JLabel)c2).setHorizontalAlignment(((JLabel)c3).getHorizontalAlignment());
}
return c2;
}
private final JTable dummy = new JTable();
});
The above code does not keep any references to renderers, so it avoids the NPE bug mentioned above. It does not require any named class, so you can just drop the code in wherever you need it.
((DefaultTableCellRenderer)jTable2.getTableHeader().getDefaultRenderer())
.setHorizontalAlignment(JLabel.CENTER);
((JLabel)mTabBOM.getTableHeader().getDefaultRenderer()).setHorizontalAlignment( JLabel.CENTER );

Change the background color of a row in a JTable

I have a JTable with 3 columns. I've set the TableCellRenderer for all the 3 columns like this (maybe not very effective?).
for (int i = 0; i < 3; i++) {
myJTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
}
The getTableCellRendererComponent() returns a Component with a random background color for each row.
How could I change the background to an other random color while the program is running?
Resumee of Richard Fearn's answer , to make each second line gray:
jTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer()
{
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
final Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
c.setBackground(row % 2 == 0 ? Color.LIGHT_GRAY : Color.WHITE);
return c;
}
});
One way would be store the current colour for each row within the model. Here's a simple model that is fixed at 3 columns and 3 rows:
static class MyTableModel extends DefaultTableModel {
List<Color> rowColours = Arrays.asList(
Color.RED,
Color.GREEN,
Color.CYAN
);
public void setRowColour(int row, Color c) {
rowColours.set(row, c);
fireTableRowsUpdated(row, row);
}
public Color getRowColour(int row) {
return rowColours.get(row);
}
#Override
public int getRowCount() {
return 3;
}
#Override
public int getColumnCount() {
return 3;
}
#Override
public Object getValueAt(int row, int column) {
return String.format("%d %d", row, column);
}
}
Note that setRowColour calls fireTableRowsUpdated; this will cause just that row of the table to be updated.
The renderer can get the model from the table:
static class MyTableCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
MyTableModel model = (MyTableModel) table.getModel();
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
c.setBackground(model.getRowColour(row));
return c;
}
}
Changing a row's colour would be as simple as:
model.setRowColour(1, Color.YELLOW);
The other answers given here work well since you use the same renderer in every column.
However, I tend to believe that generally when using a JTable you will have different types of data in each columm and therefore you won't be using the same renderer for each column. In these cases you may find the Table Row Rendering approach helpfull.
This is basically as simple as repainting the table. I haven't found a way to selectively repaint just one row/column/cell however.
In this example, clicking on the button changes the background color for a row and then calls repaint.
public class TableTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Color[] rowColors = new Color[] {
randomColor(), randomColor(), randomColor()
};
final JTable table = new JTable(3, 3);
table.setDefaultRenderer(Object.class, new TableCellRenderer() {
#Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
JPanel pane = new JPanel();
pane.setBackground(rowColors[row]);
return pane;
}
});
frame.setLayout(new BorderLayout());
JButton btn = new JButton("Change row2's color");
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
rowColors[1] = randomColor();
table.repaint();
}
});
frame.add(table, BorderLayout.NORTH);
frame.add(btn, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
private static Color randomColor() {
Random rnd = new Random();
return new Color(rnd.nextInt(256),
rnd.nextInt(256), rnd.nextInt(256));
}
}
The call to getTableCellRendererComponent(...) includes the value of the cell for which a renderer is sought.
You can use that value to compute a color. If you're also using an AbstractTableModel, you can provide a value of arbitrary type to your renderer.
Once you have a color, you can setBackground() on the component that you're returning.

Categories