I'm not sure how I should approach this, what I want to do is have these P&L cells foreground be green when the value is greater than 0 and red if less than 0. I know I need to use tablecellrenderer somehow I'm just not sure how to start.
This is the table settings:
tbl_positions = new WebTable(posdata, posHeaders)
{
public Component prepareRenderer(TableCellRenderer renderer,
int rowIndex, int vColIndex) {
Component c = super.prepareRenderer(renderer, rowIndex,
vColIndex);
if (rowIndex % 2 == 0 && !isCellSelected(rowIndex, vColIndex))
{
c.setBackground((new Color(216, 216, 216)));
}
else
{
if(!isCellSelected(rowIndex, vColIndex))
{
if(rowIndex != 21)
c.setBackground(getBackground());
else
c.setBackground((new Color(216, 249, 255)));
}
else
{
c.setBackground((new Color(0, 128, 255)));
}
}
if(vColIndex == 5 || vColIndex == 6 )
{
Integer intValue = (Integer) getValueAt(rowIndex, vColIndex);
c.setForeground(getColor(intValue));
}
else
{
c.setForeground(getForeground());
}
return c;
}
private Color getColor(Integer intValue)
{
Color color = null;
if (intValue > 0) {
color = Color.GREEN;
} else if (intValue < 0) {
color = Color.RED;
} else {
color = getForeground();
}
return color;
}
};
It's now working:
For something as simple as setting the foreground, you don't need a custom renderer. You can simply override prepareRenderer of the JTable. Something like
JTable table = new JTable(model) {
#Override
public Component prepareRenderer(TableCellRenderer renderer,
int row, int col) {
Component c = super.prepareRenderer(renderer, row, col);
if (col == PL_COLUMN) {
Integer intValue = (Integer) getValueAt(row, col);
c.setForeground(getColor(intValue));
} else {
c.setForeground(getForeground());
}
return c;
}
private Color getColor(int intValue) {
Color color = null;
if (intValue > 0) {
color = Color.GREEN;
} else if (intValue < 0) {
color = Color.RED;
} else {
color = getForeground();
}
return color;
}
};
Take some time to go over How to Use Tables: Editors and Renderers for more information on custom rendering
UPDATE: full demo
import java.awt.Color;
import java.awt.Component;
import java.util.Random;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
public class TestTableRenderer {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
NikeSaysJustDoIt();
}
});
}
private static void NikeSaysJustDoIt() {
Random random = new Random();
Object[][] data = new Object[30][7];
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
data[i][j] = (random.nextInt(65536) - 32768);
}
}
String[] cols = new String[] { "col", "col", "col", "col", "col", "col", "col" };
DefaultTableModel model = new DefaultTableModel(data, cols);
JTable table = new JTable(model) {
#Override
public Component prepareRenderer(TableCellRenderer renderer,
int row, int col) {
Component c = super.prepareRenderer(renderer, row, col);
if (col == 5 || col == 6) {
Integer intValue = (Integer) getValueAt(row, col);
c.setForeground(getColor(intValue));
} else {
c.setForeground(getForeground());
}
return c;
}
private Color getColor(int intValue) {
Color color = null;
if (intValue > 0) {
color = Color.GREEN;
} else if (intValue < 0) {
color = Color.RED;
} else {
color = getForeground();
}
return color;
}
};
JOptionPane.showMessageDialog(null, new JScrollPane(table));
}
}
UPDATE 2: Using all double values and a NumberFormat for the last row.
I left a bunch of comments that will help you to understand the code. Note that you may want to have to formatters. The image you have displays to different formats. One with currency sign and one without. Also some of your values are integers and some are decimals. So you will most likely need two formatters to get it the way you want.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
public class TableFormatRenderDemo {
public TableFormatRenderDemo() {
JFrame frame = new JFrame();
frame.add(new JScrollPane(createTable()));
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
/*
* We are using all double values. We leave the rendering to
* the renderer. The NumberFormat is used to format the text.
*/
private JTable createTable() {
Object[][] data = new Object[][] {
{ "AUD", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
{ "CAD", 0.0, 0.0, 0.0, 0.0, 45.00, -0.35 },
{ "CHF", 0.897, -1, 0.896, 0.0, -120.00, 0.00 },
{ "CHK", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
{ "Total", null, null, null, null, 20125.00, 0.00 } };
String[] cols = { "col 1", "col 2", "col 3", "col 4", "col 5", "col 6",
"col 7" };
DefaultTableModel model = new DefaultTableModel(data, cols);
JTable table = new JTable(model) {
#Override
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
};
// Set the custom renerer
table.setDefaultRenderer(Object.class, new NumberFormatRenderer());
return table;
}
private NumberFormat getFormatter() {
NumberFormat formatter = NumberFormat.getCurrencyInstance();
DecimalFormatSymbols decimalFormatSymbols = ((DecimalFormat) formatter)
.getDecimalFormatSymbols();
decimalFormatSymbols.setCurrencySymbol("$");
((DecimalFormat) formatter)
.setDecimalFormatSymbols(decimalFormatSymbols);
return formatter;
}
/**
* Custom renderer we use for the table
*/
private class NumberFormatRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 5152961981995932787L;
private static final int PL_USD_COL = 5;
private static final int DAILY_PL_COL = 6;
// TODO We may want to have a setter for this formatter
private final NumberFormat formatter = getFormatter();
private final Color POSITIVE_COLOR = new Color(65, 185, 62);
private final Color NEGATIVE_COLOR = Color.RED;
private final Color LAST_ROW_COLOR = Color.CYAN;
private final Color ALT_ROW_COLOR = Color.LIGHT_GRAY;
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
renderAlternateBackground(row, table.getBackground(), table.getRowCount());
renderForeground(row, column, table.getForeground(),
table.getRowCount(), value);
// Formats the last row with the given NumberFormat
if (row == table.getRowCount() - 1 && column != 0 && value != null) {
setText(formatter.format((Double) value));
}
// Centers the text
setHorizontalAlignment(CENTER);
return this;
}
/**
* Renders the foreground. Render only if column is one of
* the predefined "PL" columns, using the helper method
* <code>getColor</code> to determine the color to render based on the
* <code>value</code>.
*
* #param row
* #param col
* #param tableForeground
* #param rowCount
* #param value
*/
private void renderForeground(int row, int col, Color tableForeground,
int rowCount, Object value) {
if (col == PL_USD_COL || col == DAILY_PL_COL) {
setForeground(getColor((Double) value, tableForeground));
} else {
setForeground(tableForeground);
}
}
/**
* Helper method for the <code>renderForeground</code> method. Returns
* a <code>Color</code> base on the value.
* #param value
* #param tableForeground
* #return
*/
private Color getColor(double value, Color tableForeground) {
Color color = null;
if (value > 0) {
color = POSITIVE_COLOR;
} else if (value < 0) {
color = NEGATIVE_COLOR;
} else {
color = tableForeground;
}
return color;
}
/**
* Rendered alternate background color. Check for odd rows numbers.
* The last row is rendered as the select <code>LAST_ROW_COLOR</code> color.
* #param row
* #param tableBackground
* #param rowCount
*/
private void renderAlternateBackground(int row, Color tableBackground,
int rowCount) {
if (row % 2 != 0) {
setBackground(ALT_ROW_COLOR);
} else if (row == (rowCount - 1)) {
setBackground(LAST_ROW_COLOR);
} else {
setBackground(tableBackground);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TableFormatRenderDemo();
}
});
}
}
final class RenderRedGreen extends DefaultTableCellRenderer {
RenderRedGreen () {
setHorizontalAlignment(SwingConstants.RIGHT);
}
#Override public Component getTableCellRendererComponent(
JTable aTable, Object aNumberValue, boolean aIsSelected,
boolean aHasFocus, int aRow, int aColumn) {
Component renderer = super.getTableCellRendererComponent(
aTable, aNumberValue, aIsSelected, aHasFocus, aRow, aColumn
);
if (aNumberValue != null && (Double)aNumberValue < 10) { //Checking if cell´s values isnt null and the condition is true
renderer.setForeground(Color.RED);
}else{
renderer.setForeground(Color.GREEN);
}
return renderer;
}
}
//Where you are retrieving data from database
TableName.getColumnModel().getColumn(TableColumnName).setCellRenderer(new RenderRedGreen() );
Related
I would a Swing component that almost behave like a JTable for read-only data, but with interactive cells. The table might have hundreds of rows so adding components to the swing tree might not be the right choice.
Currently I'm hacking around the JTable by making the interactive cells editable. This fills hacky and misusing the API, however there's no choice there.
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Objects;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
public final class InteractiveTableCells extends JPanel {
private InteractiveTableCells() {
super(new BorderLayout());
String wagonsData = "";
Object[][] data = {
{124, wagonsData},
{13, wagonsData},
{78, wagonsData},
{103, wagonsData}
};
var model = new DefaultTableModel(data, new String[] {"Seats", "Train"}) {
#Override
public Class<?> getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
#Override
public boolean isCellEditable(int row, int column) {
return column == 1;
}
};
var table = new JTable(model);
table.setRowHeight(30);
table.setColumnSelectionAllowed(false);
var trainColumn = table.getColumnModel().getColumn(1);
trainColumn.setCellRenderer(new TrainChartPanelRenderer());
trainColumn.setCellEditor(new TrainChartPanelEditor(table));
add(new JScrollPane(table));
setPreferredSize(new Dimension(320, 240));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
var frame = new JFrame("Read-only Interactive Table Cells");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new InteractiveTableCells());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
class InteractiveChartPanel extends JPanel {
private final Rectangle wagon1 = new Rectangle(4, 2, 30, 16);
private final Rectangle wagon2 = new Rectangle(4 + 30 + 4, 2, 30, 16);
private final Rectangle wagon3 = new Rectangle(38 + 30 + 4, 2, 30, 16);
private final Rectangle wagon4 = new Rectangle(72 + 30 + 4, 2, 30, 16);
private Rectangle hoveredWagon = null;
protected InteractiveChartPanel() {
super();
setOpaque(true);
addMouseMotionListener(new MouseInputAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
var location = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(location, InteractiveChartPanel.this);
var oldHoveredWagon = hoveredWagon;
if (wagon1.contains(location)) {
hoveredWagon = wagon1;
} else if (wagon2.contains(location)) {
hoveredWagon = wagon2;
} else if (wagon3.contains(location)) {
hoveredWagon = wagon3;
} else if (wagon4.contains(location)) {
hoveredWagon = wagon4;
} else {
hoveredWagon = null;
}
if (!Objects.equals(oldHoveredWagon, hoveredWagon)) {
repaint();
}
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.ORANGE);
g2.fill(wagon1);
g2.fill(wagon2);
g2.fill(wagon3);
g2.fill(wagon4);
if (hoveredWagon != null) {
g2.setColor(Color.ORANGE.darker());
g2.fill(hoveredWagon);
}
}
}
class TrainChartPanelRenderer extends InteractiveChartPanel implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
return this;
}
}
class TrainChartPanelEditor extends AbstractCellEditor implements TableCellEditor {
private final InteractiveChartPanel panel = new InteractiveChartPanel();
protected TrainChartPanelEditor(JTable table) {
table.addMouseMotionListener(new MouseInputAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
int r = table.rowAtPoint(e.getPoint());
int c = table.columnAtPoint(e.getPoint());
if (table.isCellEditable(r, c)
&& (table.getEditingRow() != r || table.getEditingColumn() != c) // avoid flickering, when the mouse mouve over the same cell
) {
// Cancel previous, otherwise editCellAt will invoke stopCellEditing which
// actually get the current value from the editor and set it to the model (see editingStopped)
if (table.isEditing() && r >= 0 && c >= 0) {
table.getCellEditor().cancelCellEditing();
}
table.editCellAt(r, c);
} else {
if (table.isEditing() || r < 0 || c < 0) {
table.getCellEditor().cancelCellEditing();
}
}
}
});
panel.addMouseListener(new MouseInputAdapter() {
#Override
public void mouseExited(MouseEvent e) {
SwingUtilities.invokeLater(TrainChartPanelEditor.this::fireEditingCanceled);
}
});
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
panel.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
return panel;
}
#Override
public Object getCellEditorValue() {
throw new IllegalStateException("Editing should have been cancelled");
}
}
Bonus, in my case the last row might have longer content, and I'm not quite sure how to adjust the row height dynamically when the JTable is resized for example. And without triggering an infinite loop as calling setRowHeight(row) in the cell renderer can trigger a relayout and then invoke the cell renderer again.
You don’t need to deal with cell editors if you only want a hover highlighting. Just a mouse(motion)listener on the table itself, to update the affected cells, is enough.
For example
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import java.util.List;
public final class InteractiveTableCells {
public static void main(String... args) {
if(!EventQueue.isDispatchThread()) {
EventQueue.invokeLater(InteractiveTableCells::main);
return;
}
var frame = new JFrame("Read-only Interactive Table Cells");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setContentPane(createContent());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static Container createContent() {
Integer[] data = { 124, 13, 78, 103 };
var model = new AbstractTableModel() {
#Override
public int getRowCount() {
return data.length;
}
#Override
public int getColumnCount() {
return 2;
}
#Override
public String getColumnName(int column) {
return column == 0? "Seats": "Train";
}
#Override
public Class<?> getColumnClass(int column) {
return column == 0? Integer.class: String.class;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return columnIndex == 0? data[rowIndex]: "";
}
};
var table = new JTable(model);
table.setRowHeight(30);
table.setColumnSelectionAllowed(false);
var trainRenderer = new TrainRenderer();
var defCellRenderer = new DefaultTableCellRenderer();
defCellRenderer.setIcon(trainRenderer);
table.getColumnModel().getColumn(1).setCellRenderer(
(comp, value, selected, focus, row, column) -> {
trainRenderer.paintingActive = row == trainRenderer.activeRow;
return defCellRenderer.getTableCellRendererComponent(
comp, value, selected, focus, row, column);
});
var mouseListener = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
check(table.rowAtPoint(p), table.columnAtPoint(p), p);
}
#Override
public void mouseExited(MouseEvent e) {
check(-1, -1, null);
}
private void check(int r, int c, Point p) {
int lastActive = trainRenderer.activeRow;
if(c != 1) {
trainRenderer.activeRow = -1;
r = -1;
}
if(r < 0 && lastActive < 0) return;
if(r >= 0) {
var rect = table.getCellRect(r, c, false);
p.x -= rect.x;
p.y -= rect.y;
int oldCar = trainRenderer.activeCar;
if(!trainRenderer.check(p, r)) r = -1;
else if(r != lastActive || trainRenderer.activeCar != oldCar)
table.repaint(rect);
}
if(r != lastActive && lastActive >= 0) {
table.repaint(table.getCellRect(lastActive, 1, false));
}
}
};
table.addMouseMotionListener(mouseListener);
table.addMouseListener(mouseListener);
var sp = new JScrollPane(table);
sp.setPreferredSize(new Dimension(320, 240));
return sp;
}
}
class TrainRenderer implements Icon {
private final Rectangle wagon1 = new Rectangle(4, 2, 30, 16);
private final Rectangle wagon2 = new Rectangle(4 + 30 + 4, 2, 30, 16);
private final Rectangle wagon3 = new Rectangle(38 + 30 + 4, 2, 30, 16);
private final Rectangle wagon4 = new Rectangle(72 + 30 + 4, 2, 30, 16);
private final List<Rectangle> allWagons = List.of(wagon1, wagon2, wagon3, wagon4);
int activeRow = -1, activeCar = -1;
boolean paintingActive;
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D)g;
g2.translate(x, y);
for(int i = 0, num = allWagons.size(); i < num; i++) {
g2.setColor(
paintingActive && activeCar == i? Color.ORANGE.darker(): Color.ORANGE);
g2.fill(allWagons.get(i));
}
g2.translate(-x, -y);
}
boolean check(Point p, int row) {
for(int i = 0, num = allWagons.size(); i < num; i++) {
if(allWagons.get(i).contains(p)) {
activeRow = row;
activeCar = i;
return true;
}
}
activeRow = -1;
activeCar = -1;
return false;
}
#Override
public int getIconWidth() {
return wagon4.x + wagon4.width;
}
#Override
public int getIconHeight() {
return wagon4.y + wagon4.height;
}
}
Note that I also removed all unnecessary subclass relationships, compare with Prefer composition over inheritance?
By using the default cell renderer, you get the look&feel’s highlighting, as well as some performance optimizations for free.
I have written a renderer and my program does not color the cell and I don't know why. When I try to color the whole row it also colors only 3 of the 7 rows. Can someone please help me?
public void isLow(JTable jTableBestandstabelle) {
jTableBestandstabelle.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
#Override
public Component getTableCellRendererComponent(
JTable aTable, Object aNumberValue, boolean aIsSelected,
boolean aHasFocus, int aRow, int aColumn
) {
if (aNumberValue == null) {
return this;
}
Component renderer = super.getTableCellRendererComponent(
aTable, aNumberValue, aIsSelected, aHasFocus, aRow, aColumn
);
int m = (int) jTableBestandstabelle.getValueAt(aRow, 4);
int a = (int) jTableBestandstabelle.getValueAt(aRow, 5);
if (a < m && column == 5) {
renderer.setForeground(Color.black);
renderer.setBackground(Color.red);
} else {
renderer.setForeground(Color.black);
}
return this;
}
});
}
public void isLow(JTable jTableBestandstabelle) {
jTableBestandstabelle.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
#Override
public Component getTableCellRendererComponent(
JTable aTable, Object aNumberValue, boolean aIsSelected,
boolean aHasFocus, int aRow, int aColumn
) {
if (aNumberValue == null) {
return this;
}
Component renderer = super.getTableCellRendererComponent(
aTable, aNumberValue, aIsSelected, aHasFocus, aRow, aColumn
);
int m = (int) jTableBestandstabelle.getValueAt(aRow, 4);
int a = (int) jTableBestandstabelle.getValueAt(aRow, 5);
if (a < m) {
renderer.setForeground(Color.black);
renderer.setBackground(Color.red);
} else {
renderer.setForeground(Color.black);
}
return this;
}
});
}
You have two problem in your code:
You use a boolean columns in your table (last column). So if you want to present a checkbox in the column you cannot use a DefaultTableCellRenderer
When you use table.setDefaultRenderer(Object.class, renderer) this will not work for some primitive tapes like Boolean.class or Integer.class (don't know why). So you need to set the renderer for these classes too (table.setDefaultRenderer(Integer.class, renderer) and table.setDefaultRenderer(Boolean.class, renderer)). But due to the point 1 its not a good idea.
Here is mine solution:
import java.awt.Color;
import java.awt.Component;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
/**
* <code>TestTable</code>.
*/
public class TestTable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new TestTable()::startUp);
}
private void startUp() {
DefaultTableModel model = new DefaultTableModel(0, 7) {
#Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 6) {
return Boolean.class; // last column is boolean
} else if (columnIndex == 0 && columnIndex > 3) {
return Integer.class; // 1, 4, 5 are integers
}
return super.getColumnClass(columnIndex);
}
};
model.addRow(new Object[] {1, "Test1", "Test2", "Test3", 6, 5, false});
model.addRow(new Object[] {1, "Test11", "Test22", "Test33", 4, 5, true});
JTable table = new JTable(model);
for (int i = 0; i < table.getColumnCount(); i++) {
TableColumn col = table.getColumnModel().getColumn(i);
int modelCol = table.convertColumnIndexToModel(i);
col.setCellRenderer(new MyCellRenderer(table.getDefaultRenderer(model.getColumnClass(modelCol))));
}
JFrame frm = new JFrame("Table test");
frm.add(new JScrollPane(table));
frm.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frm.pack();
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
private static class MyCellRenderer implements TableCellRenderer {
private final TableCellRenderer origin;
public MyCellRenderer(TableCellRenderer origin) {
this.origin = origin;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
Component c = origin.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
int m = (int) table.getValueAt(row, 4);
int a = (int) table.getValueAt(row, 5);
if (a < m) {
c.setForeground(Color.black);
c.setBackground(Color.red);
} else {
c.setForeground(Color.black);
c.setBackground(Color.white);
}
return c;
}
}
}
Below I provide my replicable code. The problem is that it fills all cells with the gray color. However, I just need to make a gray-colored cell "moving" from column 0 to the last column (for the row 0).
Moreover, how to create more than one cell "moving" in a queue?
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.Timer;
import javax.swing.table.DefaultTableCellRenderer;
public class test2 {
/**
* #param args
*/
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
gtest t= new gtest("TEST");
f.pack();
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
}
}
class gtest extends JFrame
{
private static JTable table;
private int index;
public gtest(String title)
{
table = new JTable(6, 10);
table.setDefaultRenderer(Object.class, new PaintCell());
add(table);
startAnimation();
}
private void startAnimation() {
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
index++;
if (index > table.getRowCount() * table.getColumnCount())
index = 0;
table.repaint();
}
});
//timer.setRepeats(true);
timer.start();
}
class PaintCell extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
Component cell = super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
int id = row * table.getRowCount() + column;
cell.setBackground(id < index ? Color.LIGHT_GRAY : null);
return cell;
}
}
}
Change...
int id = row * table.getRowCount() + column;
cell.setBackground(id < index ? Color.LIGHT_GRAY : null);
To...
int checkRow = index / table.getColumnCount();
int checkCol = index % table.getColumnCount();
cell.setBackground(checkRow == row && checkCol == column ? Color.LIGHT_GRAY : null);
This will calculate the row and column based on the current value of index and the compare to the cells row/column of the cell renderer
I have a JTable and everytime I select a cell, I want to print its row and column index. I use getSelectedRow() and getSelectedColumn() methods for that reason. Running the code below:
Class TestTimeTable
public class TestTimeTable extends JFrame{
private final int rows = 10;
private final int cols = 8;
private final String daysOfTheWeek[] = {"MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"};
private final JPanel jTablePanel;
private final JScrollPane scrollPane;
private final JTable timeTable;
private final Object[][] rowData;
public TestTimeTable(){
this.rowData = new Object[this.rows][this.cols];
this.timeTable = new JTable(this.rowData,this.daysOfTheWeek){
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
this.timeTable.setRowHeight(200, 200);
this.timeTable.setFillsViewportHeight(true);
this.timeTable.setOpaque(true);
this.timeTable.setColumnSelectionAllowed(true);
this.timeTable.setRowSelectionAllowed(true);
this.timeTable.setCellSelectionEnabled(true);
this.timeTable.setDefaultRenderer(Object.class, new BoardTableCellRenderer());
this.scrollPane = new JScrollPane(this.timeTable, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
this.jTablePanel = new JPanel();
this.jTablePanel.add(this.scrollPane);
getContentPane().add(new JScrollPane(this.timeTable), BorderLayout.CENTER);
}
public int getColumn(){
return this.timeTable.getSelectedColumn();
}
public int getRow(){
return this.timeTable.getSelectedRow();
}
public JTable getTimeTable(){
return this.timeTable;
}
public void createAndShowUI(){
setSize(600, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] argas){
TestTimeTable tt = new TestTimeTable();
tt.createAndShowGUI();
}
}
Class BoardTableCellRenderer
class BoardTableCellRenderer extends DefaultTableCellRenderer {
Component c;
private static final long serialVersionUID = 1L;
private final Color selectionBlue = new Color(131,166,198);
private final MatteBorder border = new MatteBorder(1, 1, 0, 0, Color.BLACK);
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (table.isCellSelected(row, column)){
c.setBackground(this.selectionBlue);
setBorder(this.border);
} else {
c.setBackground(Color.WHITE);
}
if(isSelected){
JOptionPane.showMessageDialog(null, table.getSelectedRow() + ""+table.getSelectedColumn(),null, JOptionPane.INFORMATION_MESSAGE);
}
return c;
}
}
Everytime I select a cell, the JOptionPane frame appears and then the app freezes. So I have to interrupt it all the time. Could somebody please tell me why is this happening? What could I do to fix it?
use, override boolean isSelected, boolean hasFocus (built_in methods) from getTableCellRendererComponent instead of referencing back to JTable - table.isCellSelected(row, column), ba reseting Color for rest of cells in else statement, e.g. simple torso
.
private class StatusRenderer implements TableCellRenderer {
private static final long serialVersionUID = 1L;
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (isSelected) {
setBackground(table.getSelectionBackground());
} else {
setBackground(table.getBackground());
}
return this;
}
}
change ListSelectionMode for JTable
remove every this.Xxx, are useless
don't to setSize, use JFrame.pack() instead and/or set size for JScrollPane e.g. table.setPreferredScrollableViewportSize(table.getPreferredSize());(for reasonable numbers or rows and columns, otherwise use Dimension(x, x))
EDIT
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.UIResource;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
public class TableRolloverDemo {
private JFrame frame = new JFrame("TableRolloverDemo");
private JTable table = new JTable();
private String[] columnNames = new String[]{"Column"};
private Object[][] data = new Object[][]{{false}, {false}, {true}, {true},
{false}, {false}, {true}, {true}, {false}, {false}, {true}, {true}};
public TableRolloverDemo() {
final DefaultTableModel model = new DefaultTableModel(data, columnNames) {
//private boolean ImInLoop = false;
#Override
public Class<?> getColumnClass(int columnIndex) {
return Boolean.class;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return true;
} else {
return false;
}
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 0) {
//if (!ImInLoop) {
// ImInLoop = true;
Boolean bol = (Boolean) aValue;
super.setValueAt(aValue, rowIndex, columnIndex);
for (int i = 0; i < this.getRowCount(); i++) {
if (i != rowIndex) {
super.setValueAt(!bol, i, columnIndex);
}
}
// ImInLoop = false;
//}
} else {
super.setValueAt(aValue, rowIndex, columnIndex);
}
}
};
RolloverMouseAdapter rolloverAdapter = new RolloverMouseAdapter(table);
RolloverBooleanRenderer renderer = new RolloverBooleanRenderer(rolloverAdapter);
table.addMouseListener(rolloverAdapter);
table.addMouseMotionListener(rolloverAdapter);
table.setDefaultRenderer(Boolean.class, renderer);
table.setModel(model);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private class RolloverMouseAdapter extends MouseAdapter {
private int row = -1;
private int column = -1;
private JTable table;
public RolloverMouseAdapter(JTable table) {
this.table = table;
}
public boolean isRolloverCell(int row, int column) {
return this.row == row && this.column == column;
}
#Override
public void mouseMoved(MouseEvent e) {
int lastRow = row;
int lastColumn = column;
row = table.rowAtPoint(e.getPoint());
column = table.columnAtPoint(e.getPoint());
if (row == lastRow && column == lastColumn) {
return;
}
if (row >= 0 && column >= 0) {
table.repaint(table.getCellRect(row, column, false));
}
if (lastRow >= 0 && lastColumn >= 0) {
table.repaint(table.getCellRect(lastRow, lastColumn, false));
}
}
#Override
public void mouseExited(MouseEvent e) {
if (row >= 0 && column >= 0) {
table.repaint(table.getCellRect(row, column, false));
}
row = column = -1;
}
}
private class RolloverBooleanRenderer extends JCheckBox implements
TableCellRenderer, UIResource {
private final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
private RolloverMouseAdapter adapter;
public RolloverBooleanRenderer(RolloverMouseAdapter adapter) {
super();
this.adapter = adapter;
setHorizontalAlignment(JLabel.CENTER);
setBorderPainted(true);
}
#Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
getModel().setRollover(adapter.isRolloverCell(row, column));
if (isSelected) {
setForeground(table.getSelectionForeground());
super.setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
}
setSelected((value != null && ((Boolean) value).booleanValue()));
if (hasFocus) {
setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
} else {
setBorder(noFocusBorder);
}
return this;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TableRolloverDemo tableRolloverDemo = new TableRolloverDemo();
}
});
}
}
I have a JTable and everytime I select a cell, I want to print its row and column index.
I would override the changeSelection(...) method of the JTable:
JTable table = new JTable(data, columnNames)
{
public void xxxchangeSelection(
int row, int column, boolean toggle, boolean extend)
{
super.changeSelection(row, column, toggle, extend);
System.out.println(row + " : " + column);
}
};
Edit: I have added a SSCCE code
I have extended JTextArea in TableCellRenderer because I wanted to achieve the Multiline text wrapping Mentioned in this SO question and that is working fine. Now I have ran into weird problem with Nimbus look and feel. Nimbus is adding a border with every cell as shown in the below picture.
My Class:
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public class TestJTableMultiline extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
public TestJTableMultiline() {
super("Multi-Line Cell Example");
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
DefaultTableModel dm = new DefaultTableModel() {
/**
*
*/
private static final long serialVersionUID = 1L;
public Class<String> getColumnClass(int columnIndex) {
return String.class;
}
public boolean isCellEditable(int row, int column) {
return false;
}
};
dm.setDataVector(
new Object[][]{
{"A0, Line1\nA0, Line2\nA0, Line3",
"B0, Line1\nB0, Line2",
"C0, Line1"},
{"A1, Line1",
"B1, Line1\nB1, Line2",
"C1, Line1"},
{"A2, Line1",
"B2, Line1",
"C2, Line1"}
},
new Object[] {"A", "B", "C"});
JTable table = new JTable(dm);
table.setDefaultRenderer(String.class, new MultiLineTableCellRenderer());
TableRowSorter<? extends TableModel> sort = new TableRowSorter<DefaultTableModel>(dm);
table.setRowSorter(sort);
JScrollPane scroll = new JScrollPane(table);
getContentPane().add(scroll);
setLocationByPlatform(true);
setSize(400, 430);
setVisible(true);
}
public static void main(String[] args) {
try{
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
System.out.println("choosed nimbus");
break;
}
}
}catch(Exception e){
e.printStackTrace();
}
TestJTableMultiline frame = new TestJTableMultiline();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
}
class MultiLineTableCellRenderer extends JTextArea
implements TableCellRenderer {
/**
*
*/
private static final long serialVersionUID = 1L;
private List<List<Integer>> rowColHeight = new ArrayList<List<Integer>>();
public MultiLineTableCellRenderer() {
setLineWrap(true);
setWrapStyleWord(true);
setOpaque(true);
}
#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());
}
setFont(table.getFont());
if (hasFocus) {
setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
if (table.isCellEditable(row, column)) {
setForeground(UIManager.getColor("Table.focusCellForeground"));
setBackground(UIManager.getColor("Table.focusCellBackground"));
}
} else {
setBorder(new EmptyBorder(1, 2, 1, 2));
}
if (value != null) {
setText(value.toString());
} else {
setText("");
}
adjustRowHeight(table, row, column);
return this;
}
/**
* Calculate the new preferred height for a given row, and sets the height on the table.
*/
private void adjustRowHeight(JTable table, int row, int column) {
//The trick to get this to work properly is to set the width of the column to the
//textarea. The reason for this is that getPreferredSize(), without a width tries
//to place all the text in one line. By setting the size with the with of the column,
//getPreferredSize() returnes the proper height which the row should have in
//order to make room for the text.
int cWidth = table.getTableHeader().getColumnModel().getColumn(column).getWidth();
setSize(new Dimension(cWidth, 1000));
int prefH = getPreferredSize().height;
while (rowColHeight.size() <= row) {
rowColHeight.add(new ArrayList<Integer>(column));
}
List<Integer> colHeights = rowColHeight.get(row);
while (colHeights.size() <= column) {
colHeights.add(0);
}
colHeights.set(column, prefH);
int maxH = prefH;
for (Integer colHeight : colHeights) {
if (colHeight > maxH) {
maxH = colHeight;
}
}
if (table.getRowHeight(row) != maxH) {
table.setRowHeight(row, maxH);
}
}
}
What has been tried:
I have tried setBorder(null) and setBorder(BorderFactory.createLineBorder(Color.black)) and various other color like forground and background color of the table.
Then I looked at this SO question and tried that solution as well but I am not able to resolve the border problem. I have also tried removing setBorder call altogether but the results are same.
I have also tried removing the cell border in jtable mentioned in this SO question
How can I resolve this border problem with Nimbus look and feel.
Note: I am getting nice view with other look and feel.
This might work in JDK 1.7.0 or later:
MultiLineTableCellRenderer r = new MultiLineTableCellRenderer();
UIDefaults d = new UIDefaults();
d.put("TextArea.borderPainter", new Painter() {
#Override public void paint(Graphics2D g, Object o, int w, int h) {}
});
r.putClientProperty("Nimbus.Overrides", d);
r.putClientProperty("Nimbus.Overrides.InheritDefaults", false);
table.setDefaultRenderer(String.class, r);
SSCCE
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.*;
import javax.swing.table.*;
import javax.swing.UIManager.LookAndFeelInfo;
public class TestJTableMultiline2 {
public JComponent makeUI() {
String[] columnNames = {"A", "B", "C"};
Object[][] data = {
{"A0, Line1\nA0, Line2\nA0, Line3", "B0, Line1\nB0, Line2", "C0, Line1"},
{"A1, Line1", "B1, Line1\nB1, Line2", "C1, Line1"},
{"A2, Line1", "B2, Line1", "C2, Line1"}
};
DefaultTableModel model = new DefaultTableModel(data, columnNames) {
#Override public Class<?> getColumnClass(int column) {
return String.class;
}
#Override public boolean isCellEditable(int row, int column) {
return false;
}
};
JTable table = new JTable(model);
table.setAutoCreateRowSorter(true);
MultiLineTableCellRenderer r = new MultiLineTableCellRenderer();
UIDefaults d = new UIDefaults();
d.put("TextArea.borderPainter", new Painter() {
#Override public void paint(Graphics2D g, Object o, int w, int h) {}
});
r.putClientProperty("Nimbus.Overrides", d);
r.putClientProperty("Nimbus.Overrides.InheritDefaults", false);
table.setDefaultRenderer(String.class, r);
return new JScrollPane(table);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch(Exception e) {
e.printStackTrace();
}
JFrame f = new JFrame("Multi-Line Cell Example 2");
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new TestJTableMultiline2().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class MultiLineTableCellRenderer extends JTextArea implements TableCellRenderer {
private List<List<Integer>> rowColHeight = new ArrayList<List<Integer>>();
//private Border fhb = UIManager.getBorder("Table.focusCellHighlightBorder");
//private Border epb = BorderFactory.createEmptyBorder(2,5,2,5);
public MultiLineTableCellRenderer() {
super();
setLineWrap(true);
setWrapStyleWord(true);
setOpaque(true);
//System.out.println(fhb.getBorderInsets(this));
//setBorder(epb);
}
#Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
setFont(table.getFont());
setText(value != null ? value.toString() : "");
setMargin(new Insets(2,5,2,5));
//setBorder(hasFocus ? fhb : epb);
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
}
adjustRowHeight(table, row, column);
return this;
}
/**
* Calculate the new preferred height for a given row, and sets the height on the table.
* http://blog.botunge.dk/post/2009/10/09/JTable-multiline-cell-renderer.aspx
*/
private void adjustRowHeight(JTable table, int row, int column) {
//The trick to get this to work properly is to set the width of the column to the
//textarea. The reason for this is that getPreferredSize(), without a width tries
//to place all the text in one line. By setting the size with the with of the column,
//getPreferredSize() returnes the proper height which the row should have in
//order to make room for the text.
//int cWidth = table.getTableHeader().getColumnModel().getColumn(column).getWidth();
int cWidth = table.getCellRect(row, column, false).width; //Ignore IntercellSpacing
setSize(new Dimension(cWidth, 1000));
int prefH = getPreferredSize().height;
while (rowColHeight.size() <= row) {
rowColHeight.add(new ArrayList<Integer>(column));
}
List<Integer> colHeights = rowColHeight.get(row);
while (colHeights.size() <= column) {
colHeights.add(0);
}
colHeights.set(column, prefH);
int maxH = prefH;
for (Integer colHeight : colHeights) {
if (colHeight > maxH) {
maxH = colHeight;
}
}
if (table.getRowHeight(row) != maxH) {
table.setRowHeight(row, maxH);
}
}
}