Change color of column in swing application - java

The program is about patient and their vital signs. In case the patient has abnormal vital signs(outside the range specified) i need to highlight that column in red. I have written an isNormal function mentioned below. But I am unsure how to highlight the column in red using this function. Can somebody please suggest a way to do this?
private void refresh()
{
int rowCount = vitalSignTable.getRowCount();
int i;
for (i= rowCount-1; i>=0; i--)
{
((DefaultTableModel)vitalSignTable.getModel()).removeRow(i);
}
for(vitalSigns vs :vitalSign)
{
Object[] vital_row = new Object[6];
vital_row [0] = vs.getRespRate();
vital_row [1] = vs.getHeartRate();
vital_row [2] =vs.getBloodPressure();
vital_row [3] = vs.getWeightPound();
vital_row[4] = vs.getWeightKilos();
vital_row[5]= vs.getTimeStamp();
((DefaultTableModel)vitalSignTable.getModel()).addRow(vital_row);
}
}
private String isNormal( vitalSigns vitalSign) {
String isNormal = "Normal";
int respirationRate = vitalSign.getRespRate();
int heartRate = vitalSign.getHeartRate();
double bloodPressure = vitalSign.getBloodPressure();
int weightPound = vitalSign.getWeightPound();
int weightKilos = vitalSign.getWeightKilos();
{
if ((respirationRate < 20 || respirationRate > 30) /*Respiration Rate*/
|| (heartRate < 80 || heartRate > 130) /*Heart Rate*/
|| (bloodPressure < 80 || bloodPressure > 110) /*Blood Pressure*/
|| (weightPound <80 || weightPound > 160)
|| (weightKilos <40 || weightKilos > 80))
{
isNormal = "Abnormal";
}
}
return isNormal;
}

Here is a little example, how to color cells in a table. To do it, you need to implement your own TableCellRenderer and set it for a column. For more information see the article about JTable.
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.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
/**
* <code>TableRendererTest</code>.
*/
public class TableColumnsTest implements Runnable {
#Override
public void run() {
JFrame frm = new JFrame("Renderer test");
DefaultTableModel model = new DefaultTableModel(20, 20);
for (int i = 0; i < 20; i++) {
model.setValueAt("Test " + i, i, 3);
}
JTable table = new JTable(model);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
JScrollPane scroller = new JScrollPane(table);
frm.add(scroller);
// set renderer for the column 3
table.getColumnModel().getColumn(3).setCellRenderer(
new ColoredRenderer(new DefaultTableCellRenderer(), Color.RED, Color.WHITE));
frm.pack();
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new TableColumnsTest());
}
private static class ColoredRenderer implements TableCellRenderer {
private final TableCellRenderer delegate;
private final Color differentBackground;
private final Color differentForeground;
public ColoredRenderer(TableCellRenderer delegate, Color differentBackground, Color differentForeground) {
this.delegate = delegate;
this.differentBackground = differentBackground;
this.differentForeground = differentForeground;
}
#Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
Component result = delegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
// probably check for null values and whether the cell is selected (isSelected).
result.setBackground(differentBackground);
result.setForeground(differentForeground);
return result;
}
}
}

Related

Updating/Calling JTable rendering when a value is changed MANUALLY/DYNAMICALLY

so i want my JTable to render everytime a value in a variable is changed however this is not taking place, the table is only getting rendered either when I click on it, or move it out of view then back in. Any suggestions? I am using a custom TableCellRender as posted below.
import java.awt.Color;
import java.awt.Component;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.TableCellRenderer;
public class myRenderer extends JLabel implements TableCellRenderer {
private static final long serialVersionUID = 1L;
public myRenderer()
{
super.setOpaque(true);
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
setHorizontalAlignment(SwingConstants.CENTER);
setText(value.toString());
Color myColor = new Color(255, 253, 117);
setBackground(myColor);
if(value == Integer.valueOf(-1))
{
value = null;
setText("");
return this;
}
for(int i = 0; i < 90; i++) {
if(value == Integer.valueOf(finalClass.done[i])) //this value changes during the program.
{
setBackground(Color.cyan);
}
}
return this;
}
}
I want my table to render everytime that the value in finalClass.done is changed in any other part of the program. any ideas? i tried the revalidate option, but got no results.
EDIT:
Here is a minimal version of finalClass, which when run together with the above mentioned renderer code, can reproduce the same error I'm facing.
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class finalClass {
JFrame frame = new JFrame();
static int[] done = new int[90];
Integer[][] slip = new Integer[9][5];
String colHeader[] = {"1","2","3","4","5"};
JTable table;
finalClass() {
for(int i = 0; i<90; i++)
done[i] = -1;
int cnt = 0;
for(int x = 0; x<9; x++ ) {
for(int y = 0; y <5; y++) {
slip[x][y] = cnt++;
}
}
DefaultTableModel tableModel = new DefaultTableModel(slip, colHeader) {
private static final long serialVersionUID = 1L;
#Override
public boolean isCellEditable(int row, int column) {
//all cells false
return false;
}
};
table = new JTable(slip, colHeader);
table.setDefaultRenderer(Object.class, new myRenderer());
table.setModel(tableModel);
frame.add(table);
frame.setVisible(true);
frame.pack();
}
public static void main(String[] args) {
new finalClass();
try {
Thread.sleep(5000);
done[5] = 10;
} catch(Exception e) {
e.printStackTrace();
}
}
}
Thank you all in advance for bearing with me. I'm new here so it might take a little time for me to understand how things are done around here. sorry to waste your time and thanks for the help in advance.
The reason why the value shows only after you move the table or click on it, is that by doing so you force a repaint.
You can verify it by invoking frame.repaint() after done[5] = 10;
The following is a one-file MRE (copy paste the entire code into FinalClass.java and run):
import java.awt.Color;
import java.awt.Component;
import java.util.Arrays;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
public class FinalClass {
private final JFrame frame = new JFrame();
private final int[] done = new int[90]; //better avoid static
private final Integer[][] slip = new Integer[9][5];
private final String colHeader[] = {"1","2","3","4","5"};
private JTable table;
FinalClass() {
Arrays.fill(done, -1);
int cnt = 0;
for(int x = 0; x<9; x++ ) {
for(int y = 0; y <5; y++) {
slip[x][y] = cnt++;
}
}
DefaultTableModel tableModel = new DefaultTableModel(slip, colHeader) {
private static final long serialVersionUID = 1L;
#Override
public boolean isCellEditable(int row, int column) {
//all cells false
return false;
}
};
table = new JTable(slip, colHeader);
table.setDefaultRenderer(Object.class, new MyRenderer());
table.setModel(tableModel);
frame.add(table);
frame.pack();
frame.setVisible(true);
}
private void refresh(){
frame.repaint();
}
void done(int index, int value){
done[index] = value;
refresh();
}
public static void main(String[] args) {
FinalClass f = new FinalClass();
try {
Thread.sleep(3000);
f.done(5, 10);
} catch(Exception e) { e.printStackTrace(); }
}
//follow java naming conventions
class MyRenderer extends JLabel implements TableCellRenderer {
private static final long serialVersionUID = 1L;
public MyRenderer()
{
super.setOpaque(true);
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
setHorizontalAlignment(SwingConstants.CENTER);
setText(value.toString());
Color myColor = new Color(255, 253, 117);
setBackground(myColor);
if((int)value == -1)
{
value = null;
setText("");
return this;
}
for(int i = 0; i < 90; i++) {
if((int)value == done[i]) //this value changes during the program.
{
setBackground(Color.cyan);
}
}
return this;
}
}
}
Side notes:
1. If you want the table to respond automatically to change in the undelying data, you need to apply the change to its model as demonstrated in this answer to your previous question.
2.Modifying done from more than one thread needs to be synchronized.
3.It is recommended to follow Java Naming Conventions

How to get the exact space that text takes inside a cell of a JTable?

I have a row header inside a JScrollPane of which I should set the width according to the maximum width that the text takes. I managed to do so but I have to add 1 in my call to setPreferredScrollableViewportSize in order to display the full text. Did I get the measurement wrong? Or is this quantity fixed?
This is the main class.
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.WindowConstants;
public class Example {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
JTable table = new JTable(5, 3);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
HeaderColumn ch = new HeaderColumn(5, 1);
JFrame frame = new JFrame();
JScrollPane sp = new JScrollPane(table);
sp.setRowHeaderView(ch);
frame.add(sp);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
ch.adjustSize();
}
});
}
}
This is the header class. Please look at adjustSize().
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import javax.swing.BorderFactory;
import javax.swing.JTable;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
public class HeaderColumn extends JTable{
private final int INSET = 2;
public HeaderColumn(int r, int c){
super(r,c);
}
#Override
public void columnAdded(TableColumnModelEvent e){
DefaultTableModel m = (DefaultTableModel)getModel();
int rowCount = m.getRowCount();
for(int i = 0; i < rowCount; i++){
m.setValueAt("#" + Integer.toString(i+1), i, 0);
}
TableColumn tc = getColumnModel().getColumn(e.getToIndex());
tc.setCellRenderer(new DefaultTableCellRenderer(){
#Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
setText((value == null) ? "" : value.toString());
setBorder(BorderFactory.createEmptyBorder(0, INSET, 0, INSET));
return this;
}
});
}
public void adjustSize(){
FontMetrics fm = getGraphics().getFontMetrics(getTableHeader().getFont());
int maxTextWidth = 0;
DefaultTableModel m = (DefaultTableModel)getModel();
int rowCount = m.getRowCount();
for(int i = 0; i < rowCount; i ++){
int width = fm.stringWidth(m.getValueAt(i, 0).toString());
if(width > maxTextWidth){
maxTextWidth = width;
}
}
setPreferredScrollableViewportSize(new Dimension(maxTextWidth+INSET*2+1, 0));
}
}
I managed to do so but I have to add 1 in my call to setPreferredScrollableViewportSize in order to display the full text. Did I get the measurement wrong? Or is this quantity fixed?
I think you need to include the getIntercellSpacing() value returned from the table.
Check out Table Column Adjuster for more information and example code for determining the column width.

Jtable with conditional formatting like Excel

Does JTable supports 3 color conditional formatting like excel does.
Example:
I checked on google and all I could find assigning single color at a time to a cell which matches the criteria. I was wondering if I could just provide three colors and Jtable would assign shades of color according to the value of the cell.
Here is one of the ways, how you can do this. Additional classes we can find here.
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.AbstractListModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import static javax.swing.SwingConstants.CENTER;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import general.VerticalTableHeaderCellRenderer;
import java.awt.Color;
import java.util.Random;
import javax.swing.table.TableCellRenderer;
// The class of template for table's presentation of knowledge
//==============================================================================
public class ExcelTable extends JFrame {
public static int rowCount = 1024;
public static int colCount = 1024;
public static int CHLength = 100;
public static int RWLength = 50;
public static int DCW = 20;
// The constructor of this class
//==============================================================================
public ExcelTable() {
super("Draw cell rotate");
setSize(400, 400);
//The abstract model of table's presentation
ListModel lm = new AbstractListModel() {
String[] headers = new String[rowCount];
#Override
public int getSize() {
return headers.length;
}
#Override
public Object getElementAt(int index) {
return headers[index];
}
};
DefaultTableModel dtm = new DefaultTableModel(lm.getSize(), colCount);
JTable table = new JTable(dtm) {
#Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int col) {
Component comp = super.prepareRenderer(renderer, row, col);
Object value = getModel().getValueAt(row, col);
setCellSelectionEnabled(true);
// Here you can write you schemes
final Random r = new Random();
Color c = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256), r.nextInt(256));
if (getSelectedRow() != 0) {
if (row % 2 == 0 && col % 2 == 1) {
comp.setBackground(c);
} else if (row % 2 == 1 && row % 2 == 1) {
comp.setBackground(c);
} else {
comp.setBackground(c);
}
} else {
comp.setBackground(Color.white);
}
return comp;
}
};
table.getTableHeader().setDefaultRenderer(new VerticalTableHeaderCellRenderer());
for (int i = 0; i < colCount; i++) {
table.getColumnModel().getColumn(i).setPreferredWidth(DCW);// .setTotalColumnWidth();
}
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.setCellSelectionEnabled(true);
// Create header rotation
JList rowHeader = new JList(lm);
// if (fieldlength)
rowHeader.setFixedCellWidth(RWLength); //RowHeaser width
rowHeader.setFixedCellHeight(table.getRowHeight());
//Set render
rowHeader.setCellRenderer(new RowRenderer(table));
//JScrollPane
JScrollPane pane = new JScrollPane(table);
pane.setColumnHeader(new JViewport() {
#Override
public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
d.height = CHLength; // Col header Height
return d;
}
});
pane.setRowHeaderView(rowHeader);
getContentPane().add(pane, BorderLayout.CENTER);
}
//=========================================
public static void main(String[] args) {
ExcelTable jr = new ExcelTable();
jr.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
jr.setVisible(true);
}
}
class RowRenderer extends JLabel implements ListCellRenderer {
public RowRenderer(JTable table) {
JTableHeader header = table.getTableHeader();
setOpaque(true);
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
setHorizontalAlignment(CENTER);
setForeground(header.getForeground());
setBackground(header.getBackground());
setFont(header.getFont());
}
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
setText((value == null) ? "" : value.toString());
return this;
}
}
OUTPUT:
also:
or
The desired color scheme you must implement into override method:
#Override
public Component prepareRenderer
(TableCellRenderer renderer, int row, int col
) {
Component comp = super.prepareRenderer(renderer, row, col);
Object value = getModel().getValueAt(row, col);
setCellSelectionEnabled(true);
// Here you can write you schemes in RGB
// of course you need to describe it before mathematicaly
// and using variable `row` and `col` you will have coordinates to the cells
final Random r = new Random();
int R = r.nextInt(Math.round(256 / (row + 1)));
int G = r.nextInt(Math.round(256 / (col + 1)));
int B = r.nextInt(Math.round(256 / (row + col + 1)));
Color c = new Color(R, G, B);
if (getSelectedRow() != 0) {
if (row % 2 == 0 && col % 2 == 1) {
comp.setBackground(c);
} else if (row % 2 == 1 && row % 2 == 1) {
comp.setBackground(c);
} else {
comp.setBackground(c);
}
} else {
comp.setBackground(Color.white);
}
return comp;
}
};

Swing - Is it possible to set the font color of 'specific' text within a JTable cell?

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

Widgets in JTable cells

Widgets in JTable columns are expected to be not distinguishable from normal ones, right? There seems to be behavioral difference, take Swing documentation example and move mouse over checkboxes in the Vegetarian column... They don't react at all. I understand that those are just widget surrogates, so highlighting has to be done manually, so how would I fix this? I tried widget.requestFocusInWindow();
in mouseMoved() for the surrogate widget event handler without success. Any other workaround?
You can create your own cell renderer that applies a rollover effect. Then, add a mouse listener that tracks the mouse movement and repaints the relevant cells. You need to apply the effect to the current cell under the cursor and clear the previous cell.
Below is a short example that demonstrates this approach on a checkbox renderer. The example extends default BooleanRenderer. The only change is getModel().setRollover(...) in getTableCellRendererComponent().
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 static void createAndShowGUI() {
JFrame frame = new JFrame("TableRolloverDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTable table = new JTable();
final DefaultTableModel model = new DefaultTableModel(new Object[][] {
{ false }, { false }, { true }, { true } },
new Object[] { "Column" }) {
public Class<?> getColumnClass(int columnIndex) {
return Boolean.class;
}
};
RolloverMouseAdapter rolloverAdapter = new RolloverMouseAdapter(table);
RolloverBooleanRenderer renderer = new RolloverBooleanRenderer(rolloverAdapter);
table.addMouseListener(rolloverAdapter);
table.addMouseMotionListener(rolloverAdapter);
table.setDefaultRenderer(Boolean.class, renderer);
table.setModel(model);
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
static 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;
}
}
static class RolloverBooleanRenderer extends JCheckBox implements
TableCellRenderer, UIResource {
private static 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);
}
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() {
public void run() {
createAndShowGUI();
}
});
}
}
It's rollover (or lack of thereof) which makes widgets in the table to appear dead. If JPanel is genuine container of widgets, why JTable and JTree are not?
Both JTable and JTree use the flyweight pattern for rendering. The default renderer & editor have no intrinsic mouse-over behavior. You have to supply the desired behavior yourself, as #Max shows.
Jtable dont put real components into cells. They only use the component's paint methods to render the cell content.

Categories