Scrollable Cells in JTable - java

I have a Jtable in which I have to show some big data. I cann't increase the size of the Cells So I need to add a scrollbar in each cell of the table through which I can scroll the text of cells.
I have tried to add a Custom Cell Renderer
private class ExtendedTableCellEditor extends AbstractCellEditor implements TableCellEditor
{
JLabel area = new JLabel();
String text;
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int rowIndex, int vColIndex)
{
area.setText(text);
return new JScrollPane(area);
}
public Object getCellEditorValue()
{
return text;
}
}
Now I am able to see the Scroll bar on the cells but not able to click and scroll them.
Any suggestions to this issue will be great.
Thanks in Advance.

Adding a JScrollPaneand placing the JLabel in the JScrollPane solved the issue. So I would like to share it with you all.
private class ExtendedTableCellEditor extends AbstractCellEditor implements TableCellEditor
{
JLabel _component = new JLabel();
JScrollPane _pane = new JScrollPane(_component, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
/**
* Returns the cell editor component.
*
*/
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int rowIndex, int vColIndex)
{
if (value == null) return null;
_component.setText(value != null ? value.toString() : "");
_component.setToolTipText(value != null ? value.toString() : "");
_component.setOpaque(true);
_component.setBackground((isSelected) ? Color.BLUE_DARK : Color.WHITE);
_component.setForeground((isSelected) ? Color.WHITE : Color.BLACK);
_pane.setHorizontalScrollBar(_pane.createHorizontalScrollBar());
_pane.setVerticalScrollBar(_pane.createVerticalScrollBar());
_pane.setBorder(new EmptyBorder(0,0,0,0));
_pane.setToolTipText(value != null ? value.toString() : "");
return _pane;
}
public Object getCellEditorValue()
{
return _component.getText();
}
}

Related

how to use custom cell rendering in jtable for the below needs? [duplicate]

I would like to make an editable table and then check the data to make sure its valid. Im not sure how to change the color of just one cell. I would like to get a cell, for example (0,0) and color the foreground to red. I have read the other posts on SO as well as Oracle about the custom ColorRenderer, but i just don't get how i would use this.
Thanks.
Say that the cell you would like to render with a different color represents a status (I'll take Rejected and Approved as examples). I'd then implement a method in my table model called getStatus(int row) which returns the status for any given row.
Then, when that is in place, I'd go about creating a cell renderer responsible for rendering the column which the cell belongs to. The cell renderer would be something in the lines of the below code.
public class StatusColumnCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
//Cells are by default rendered as a JLabel.
JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
//Get the status for the current row.
CustomTableModel tableModel = (CustomTableModel) table.getModel();
if (tableModel.getStatus(row) == CustomTableModel.APPROVED) {
l.setBackground(Color.GREEN);
} else {
l.setBackground(Color.RED);
}
//Return the JLabel which renders the cell.
return l;
}
Then, when the renderer is in place, simply "apply" the renderer to the table with the following piece of code:
Table.getColumnModel().getColumn(columnIndex).setCellRenderer(new StatusColumnCellRenderer());
With regard to making a cell editable, simply implement the isCellEditable(int rowIndex, int columnIndex) method in your table model. You also need to implement the method
setValueAt(Object value, int rowIndex, int columnIndex) if you would like to keep the value which the user provides (which i assume you do!).
I would like to make an editable table and then check the data to make sure its valid.
Another approach would be to edit the data before it is saved to the table model to prevent invalid data from being entered.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
public class TableEdit extends JFrame
{
TableEdit()
{
JTable table = new JTable(5,5);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollpane = new JScrollPane(table);
getContentPane().add(scrollpane);
// Use a custom editor
TableCellEditor fce = new FiveCharacterEditor();
table.setDefaultEditor(Object.class, fce);
}
class FiveCharacterEditor extends DefaultCellEditor
{
FiveCharacterEditor()
{
super( new JTextField() );
}
public boolean stopCellEditing()
{
try
{
String editingValue = (String)getCellEditorValue();
if(editingValue.length() != 5)
{
JTextField textField = (JTextField)getComponent();
textField.setBorder(new LineBorder(Color.red));
textField.selectAll();
textField.requestFocusInWindow();
JOptionPane.showMessageDialog(
null,
"Please enter string with 5 letters.",
"Alert!",JOptionPane.ERROR_MESSAGE);
return false;
}
}
catch(ClassCastException exception)
{
return false;
}
return super.stopCellEditing();
}
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
Component c = super.getTableCellEditorComponent(
table, value, isSelected, row, column);
((JComponent)c).setBorder(new LineBorder(Color.black));
return c;
}
}
public static void main(String [] args)
{
JFrame frame = new TableEdit();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
I believe the correct way to do colouring in a table is via a ColorHighlighter. The table renderers have problems to render different colours in the same column.
Here is an example of how to use highlighters. In this case it is for highlighting a cell that is not editable.
public class IsCellEditablePredicate implements HighlightPredicate {
private JXTable table;
public IsCellEditablePredicate (final JXTable table) {
this.table = table;
}
#Override
public boolean isHighlighted(Component component, ComponentAdapter componentAdapter) {
return !table.isCellEditable(componentAdapter.row,
componentAdapter.column);
}
}
and then in your code for setuping the table you add the highlighter and its colour parameters:
ColorHighlighter grayHighlighter = new ColorHighlighter(new IsCellEditablePredicate(table));
grayHighlighter.setBackground(Color.LIGHT_GRAY);
grayHighlighter.setForeground(table.getForeground());
grayHighlighter.setSelectedBackground(table.getSelectionBackground().darker());
grayHighlighter.setSelectedForeground(table.getSelectionForeground().darker());
table.setHighlighters(grayHighlighter);
This is the simplest way to color a particular Column or cell in a jTable.
First just create a simple class of CustomRenderer
class CustomRenderer extends DefaultTableCellRenderer <br />
{
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);
setForeground(Color.blue); >
return c;
}
}
This code gets the column of cell to render
TableColumn col = tblExamHistoryAll.getColumnModel().getColumn(5);
DefaultTableModel model3 = (DefaultTableModel)tblExamHistoryAll.getModel();
col.setCellRenderer(new CustomRenderer());
This is to clear all previous rows from your table. If you do not want them just remove these lines
model3.getDataVector().removeAllElements();
model3.fireTableDataChanged();
#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);
int control = row;
control = control % 2;
control = (control == 0) ? 1 : 0;
if (control == 1) {
c.setBackground(Color.green);
} else {
c.setBackground(Color.cyan);
}
return c;
}
The most straightforward way is to write a simple TableCellRenderer by extending the DefaultTableCellRenderer and overwriting the getTableCellRendererComponent method to setBackground( Color.RED ). For example:
final JTable table = new JTable(...);
table.setCellRenderer( new DefaultTableCellRenderer() {
public Component getTableCellRenderer(JTable table, Object value, ...) {
super.getTableCellRenderer(...);
if ( value should be highlighted ) {
setBackground( Color.RED );
}
return this;
}
});
You can extend DefaultTableCellRenderer, override getTableCellRendererComponent and call something like
if (myConditions) setBackground(myColor);
before returning "this" when conditions apply but it has a very annoying side-effect of changing the default back-color due to the way DefaultTableCellRenderer.setBackGround is coded.
The trick I found was to entirely duplicate the code of DefaultTableCellRenderer in a class named HackedDefaultTableCellRenderer, add a method that calls directly the Component's setBackground implementation:
public void setComponentBackground(Color c) {
super.setBackground(c);
}
then derive my customized rendered from this hacked class instead of from DefaultTableCellRenderer, and finally call setComponentBackground instead of setBackground in my customized getTableCellRendererComponent.
The drawback is that this HackedDefaultTableCellRenderer relies on a snapshot of DefaultTableCellRenderer.

Unable to lose jComboBox in a jTable focus

I have a jTable that has a jComboBox column. However I had an issue where I couldn't get the jComboBox to show unless you first click on it. Other people have had the same problem, it seems.
So I then learned I needed to make a CellRenderer besides the CellEditor. And so I did...
public class MyCellRenderer extends JComboBox<CustomItem> implements TableCellRenderer{
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column){
if(isSelected){
setForeground(table.getSelectionForeground());
super.setBackground(table.getSelectionBackground());
} else{
setForeground(table.getForeground());
setBackground(table.getBackground());
}
setSelectedItem(value);
return this;
}
}
By the way, I am also using a ComboBoxRenderer because I need it to display text while containing an item.
public class MyComboBoxRenderer extends BasicComboBoxRenderer{
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus){
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if(value != null){
CustomItem customItem = (CustomItem) value;
setText(customItem.getText());
}
if(index == -1){
CustomItem customItem = (CustomItem) value;
setText("" + customItem.getText());
}
return this;
}
}
And now it works! It does show the jComboBox in my jTable by default BUT now each jComboBox on each row has permanent focus! And only the first one I click on actually displays the menu. The rest are 'selected' but they do not respond.
Edit: Here is how I create the table.
private void initializeTable(){
JTable jTable1 = new javax.swing.JTable();
DefaultTableModel dtm = new DefaultTableModel();
dtm.addColumn("one");
dtm.addColumn("two");
dtm.addColumn("three");
jTable1.setModel(dtm);
JComboBox<CustomItem> items = new JComboBox<>();
items.setRenderer(new MyComboBoxRenderer());
items.add(new CustomItem(1, "soup", false);
items.add(new CustomItem(33, "sauce", false);
items.setSelectedIndex(0);
jTable1.getColumnModel().getColumn(2).setCellEditor(new DefaultCellEditor(items));
jTable1.getColumnModel().getColumn(2).setCellRenderer(new MyCellRenderer());
}

Custom TableCellRenderer/TreeTableCellRenderer doesn't render Table cells

I made this CustomCellRenderer class intended to be used in JXTreeTable and JXTable objects since I have many of these in my project. So this class implements TreeCellRenderer and TableCellRenderer interfaces:
public class CustomCellRenderer extends JLabel
implements TreeCellRenderer, TableCellRenderer {
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
setBackground(selected ? new Color(83,142,213) : Color.white);
setForeground(selected ? Color.white : Color.black);
//here is the icon setting code but it's irrelevant to my problem
setText(value != null ? value.toString() : "<null>");
return this;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setBackground(isSelected ? new Color(83,142,213) : Color.white);
setForeground(isSelected ? Color.white : Color.black);
setText(value != null ? value.toString() : "<null>");
return this;
}
}
And here is the code where I set the renderer:
jXTreeTableConsumos.setDefaultRenderer(Object.class, new CustomCellRenderer());
jXTreeTableConsumos.setTreeCellRenderer(new CustomCellRenderer());
I'm expecting background and foreground become blue and white respectively when a row is selected. However it only happens at Tree table cell (first column) while only foreground changes and background stills white in the other cells in the very same selected row:
Could anybody please tell me why cells (that are not tree cells) don't change their background color?
Thanks everybody for your comments and suggestions. I found the solution in JComponent#setBackground(Color bg) documentation:
Sets the background color of this component. The background color is
used only if the component is opaque, and only by subclasses of
JComponent or ComponentUI implementations. Direct subclasses of
JComponent must override paintComponent to honor this property.
Since my CustomCellRenderer extends from JLabel the only thing I have to do is make sure it's opaque and its background color will be painted:
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setOpaque(true);//adding this line I solved my problem
setBackground(isSelected ? new Color(83,142,213) : Color.white);
setForeground(isSelected ? Color.white : Color.black);
setText(value != null ? value.toString() : "<null>");
return this;
}

Overriding createToolTip() in a custom CellRenderer

I'm trying to get a custom ToolTip for a specific column of a JTable. I've already created a CellRenderer (of which I've been changing other cell-specific attributes successfully):
private class CustomCellRenderer extends DefaultTableCellRenderer
{
private static final long serialVersionUID = 1L;
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
JComponent c = (JComponent) super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
if (value != null)
{
if(column == 1 && value instanceof Date)
{
final DateFormat df = new SimpleDateFormat("h:mm aa");
table.setValueAt(df.format(value), row, column);
}
else if(column == 2)
{
c.setToolTipText((String) value);
}
else if(column == 4)
{
final Mail m = main.selectedPage.messages.get(row);
JCheckBox checkBox;
if((Boolean) value)
{
checkBox = new JCheckBox()
{
#Override
public JToolTip createToolTip()
{
System.out.println("Passed");
return new ImageToolTip(m.getImage());
}
};
checkBox.setToolTipText(m.attachName);
}
else
checkBox = new JCheckBox();
checkBox.setSelected(((Boolean)value).booleanValue());
c = checkBox;
}
}
else
{
c.setToolTipText(null);
}
return c;
}
}
When I override any other JComponent's createTooltip() method like so, it all works fine outside of the Renderer.
checkBox = new JCheckBox()
{
#Override
public JToolTip createToolTip()
{
System.out.println("Passed");
return new ImageToolTip(m.getImage());
}
};
From what I can tell, the tooltip is created elsewhere, because "Passed" is never even printed. The checkBox.setToolTipText(m.attachName); only results in a default ToolTip with that String.
I've found someone with a similar question, but I can't say I completely understand the only resolving answer. Do I need to extend JTable and override getToolTipText(MouseEvent e)? If so, I'm not sure what with to get the correct (mine) Tooltip.
Please excuse any of my self-taught weirdness. Thanks in advance. :-)
EDIT:
Thanks to Robin, I was able to piece together something based on JTable's getToolTipText(MouseEvent e) code. I'll leave it here for anyone else with a similar problem. Again, I'm not sure it this it the best way to do it, so feel free to critique it below. :-)
messageTable = new JTable()
{
#Override
public JToolTip createToolTip()
{
Point p = getMousePosition();
// Locate the renderer under the event location
int hitColumnIndex = columnAtPoint(p);
int hitRowIndex = rowAtPoint(p);
if ((hitColumnIndex != -1) && (hitRowIndex != -1))
{
TableCellRenderer renderer = getCellRenderer(hitRowIndex, hitColumnIndex);
Component component = prepareRenderer(renderer, hitRowIndex, hitColumnIndex);
if (component instanceof JCheckBox)
{
Image img = main.selectedPage.messages.get(hitRowIndex).getImage();
if(((JCheckBox) component).isSelected())
return new ImageToolTip(img);
}
}
return super.createToolTip();
}
}
You are not able to create tooltip for checkbox inside cell renderer. Actually that component doesn't exists at the moment you are trying to move mouse over it. It is just an image. You need to create tooltip for your JTable
private void tableMouseMoved(java.awt.event.MouseEvent evt) {
String toolTipText;
int row = table.rowAtPoint(evt.getPoint());
int column = table.columnAtPoint(evt.getPoint());
if (row >= 0) {
Object o = table.getValueAt(row, column);
if (column == YourTableModel.COLUMN_INDEX_WITH_CHECKBOX) {
Boolean value = (Boolean) o;
if (value == Boolean.TRUE) {
toolTipText = "Tooltip text for true value";
} else {
toolTipText = "Tooltip text for false value";
}
}
}
}
And you need to register listener for MouseEvent of course:
javax.swing.JTable table = new JTable();
table.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseMoved(java.awt.event.MouseEvent evt) {
tableMouseMoved(evt);
}
});
The reason that the JTable does not use your tooltip can be seen in the implementation. The JTable will indeed use the component returned by the renderer, but it will ask it for its tooltip text. So only settings a custom tooltip text will work if you stick to the default JTable implementation. Just a quick copy-paste of the relevant part of the JTable source code to illustrate this:
if (component instanceof JComponent) {
// Convert the event to the renderer's coordinate system
Rectangle cellRect = getCellRect(hitRowIndex, hitColumnIndex, false);
p.translate(-cellRect.x, -cellRect.y);
MouseEvent newEvent = new MouseEvent(component, event.getID(),
event.getWhen(), event.getModifiers(),
p.x, p.y,
event.getXOnScreen(),
event.getYOnScreen(),
event.getClickCount(),
event.isPopupTrigger(),
MouseEvent.NOBUTTON);
tip = ((JComponent)component).getToolTipText(newEvent);
}
So yes, you will have to override the JTable method if you really want an image as tooltip for your check box.
On a side-note: your renderer code has weird behavior. The
final DateFormat df = new SimpleDateFormat("h:mm aa");
table.setValueAt(df.format(value), row, column);
seems incorrect. You should replace the setValueAt call by a
JLabel label = new JLabel();//a field in your renderer
//in the getTableCellRendererComponent method
label.setText( df.format( value ) );
return label;
or something similar. The renderer should not adjust the table values, but create an appropriate component to visualize the data. In this case a JLabel seems sufficient. And as Stanislav noticed in the comments, you should not constantly create new components. That defeats the purpose of the renderer which was introduced to avoid creating new components for each row/column combination. Note that the method is called getTableCellRendererComponent (emphasis on get) and not createTableCellRendererComponent

JTable data is invisible unless cell is selected

I'm creating a JTable with data contained in 2 Vector, rowData and columnNames. I'm using a renderer to give the colour I want to the JTable. But data is invisible unless I click a cell: then only that cell data is visible.
My code:
// Creating table
final JTable tablaCurvas = new JTable();
// Applng colours and column sizes with renderer
TableCellRenderer tableRender = new TableRenderer();
tablaCurvas.setDefaultRenderer(Object.class, tableRender);
// Create an easy model to add data to table
tablaCurvas.setModel(new DefaultTableModel(rowData, columnNames){
private static final long serialVersionUID = 1L;
#Override
public boolean isCellEditable(int row, int column) {
//Only the second column
return column == 1;
}
});
// Necessary clicks to edit cell
((DefaultCellEditor) tablaCurvas.getDefaultEditor(Object.class)).setClickCountToStart(1);
// Add table into a scrollPane
JScrollPane scrollPane = new JScrollPane(tablaCurvas);
// Fill the pane
tablaCurvas.setFillsViewportHeight(true);
// Preferred size
tablaCurvas.setPreferredScrollableViewportSize(new Dimension(150,100));
And the renderer:
class TableRenderer extends DefaultTableCellRenderer
{
private static final long serialVersionUID = 1L;
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
{
setEnabled(table == null || table.isEnabled());
if (column == 0)
setHorizontalAlignment(LEFT);
else // (column == 1)
setHorizontalAlignment(RIGHT);
for (int i=0; i<2; i++) {
TableColumn columna = table.getColumnModel().getColumn(i);
if (i==0){
columna.setPreferredWidth(150);
}
if (i==1) columna.setPreferredWidth(50);
}
setBackground(table.getBackground());
setForeground(table.getForeground());
if (row%2==1) setBackground(java.awt.Color.white);
else setBackground(new java.awt.Color(211, 217, 255));
return this;
}
Any way, appart from this, I'm finding much more difficult to learn how to use JTables than other Objects, because Oracle Tutorial is not very well explained in that chapter. Any book-chapter or online tutorial for JTables recommended?
You have to set the text for the DefaultTableCellRenderer component.
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
//.... your code
setText(value != null ? value.toString() : ""); // suppress null values
return this;
}
This screenshot was taken with some example data:

Categories

Resources