Add content to cell when user clicks it - java

I have a functionality in a JTable, that when user clicks the cell, it removes certain character in it (like when there's content - Hello, when user clicks it, it shows Hello). When it's no longer edited, it shows - Hello again.
My problem is that when some cell is selected (but not being edited yet) and I start typing Hi, it doesn't remove the character, so the editable cell looks like - Hello Hi.
Same problem is when some cell is selected and user presses space key.
I want to add the functionality to the JTable, so that when the content of the cell starts to be edited (by any way - clicking/typing when selected/space key/and maybe there are more options I don't know about), I want to programatically change the content first. Another option would be removing it when the cell is selected (but then I have to remember position of the last selected cell, so that the character could be readded to it).
I've tried in propertyChange in class TableChangeListener:
table.setValueAt(removeCharacter(table.getValueAt(row,column)), row, column);
but it doesn't work as the cell is already being edited and I cant change it.

You will have to use your own implementation of Cell Editor to meet your own set of requirements.
So create a custom CellEditor implementing FocusLisetener and ActionListener and implement the FocusGained and FocusLost function
Implement the actionPerformed function too to update value on enter click.
Handling the Focus event is a little bit tricky. As it tends to update the cell wrongly. That is why i had to pass the reference table to the CellEditor as a constructor parameter and read the cell row, col on Focus gain.
To reflect the - xxxx: placing - before the cell value, try using a custom CellRenderer. Check out the official tutorial page for details with example. And the part of the credit goes to #mKobel.
An implemented custom cell editor for direction: assign it to your target table column and test.
Giff of my test result:
Code:
class CustomRenderer extends DefaultTableCellRenderer {
public void setValue(Object value)
{
setText("- "+value);
}
}
class MyCellEditor extends AbstractCellEditor
implements TableCellEditor,
FocusListener,
ActionListener
{
JTextField textFeild;
String currentValue;
JTable table;
int row, col;
public MyCellEditor(JTable table) {
this.table = table;
textFeild = new JTextField();
textFeild.addActionListener(this);
textFeild.addFocusListener(this);
}
#Override
public Object getCellEditorValue() {
return currentValue;
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
currentValue = (String)value;
return textFeild;
}
#Override
public void focusGained(FocusEvent e) {
textFeild.setText("");
row = table.getSelectedRow();
col = table.getSelectedColumn();
}
#Override
public void focusLost(FocusEvent e) {
if(!textFeild.getText().equals(""))
//currentValue = textFeild.getText();
table.setValueAt(textFeild.getText(), row, col);
fireEditingStopped();
}
#Override
public void actionPerformed(ActionEvent e) {
if(!textFeild.getText().trim().equals(""))
currentValue = textFeild.getText();
fireEditingStopped();
}
}

I think you should not change the content of the cell at all.
What you need is to set on the table a TableCellRenderer that renders the cell values. Implement the cell renderer so that it shows the value "- Hello" (although your actual data could contain just "Hello"). The renderer just shows any component you want in the table. When user starts editing the cell, the renderer component is not shown. Actually you could also manipulate the editing component using a TableCellEditor.

Extracted from this comment:
The table shows prices like "$5" "20€" and when user clicks the cell
to change the price, I'd like the sign to disappear. When user
finishes editing (clicks enter or by other way), I want the symbol to
appear again.
Although #Sage post is a really great and general solution (+1 for you :), in this particular case I'd implement a TableCellRenderer and TableCellEditor using JFormattedTextField which can manage the currency format matter, as follows:
Set a generic number format to the renderer component: NumberFormat.getNumberInstance()
Set a currency number format to the editor component: NumberFormat.getCurrencyInstance()
This way when the cell is displayed the currency sign will be shown but when the cell is being edited the currency sign will "disappear".
Take a look to this example of implementation:
import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.text.NumberFormat;
import java.util.EventObject;
import java.util.Locale;
import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.JFormattedTextField;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.NumberFormatter;
public class CurrencyEditor extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {
JFormattedTextField editor;
JFormattedTextField renderer;
Integer clickCountToStart = 2;
public CurrencyEditor(Locale locale) {
initEditor(locale);
initRenderer(locale);
}
private void initRenderer(Locale locale) {
NumberFormat format = locale != null ?
NumberFormat.getCurrencyInstance(locale) : NumberFormat.getCurrencyInstance();
NumberFormatter formatter = new NumberFormatter(format);
formatter.setMinimum(Double.MIN_VALUE);
formatter.setMaximum(Double.MAX_VALUE);
formatter.setAllowsInvalid(false);
renderer = new JFormattedTextField(formatter);
}
private void initEditor(Locale locale) {
NumberFormat format = locale != null ?
NumberFormat.getNumberInstance(locale) : NumberFormat.getNumberInstance();
NumberFormatter formatter = new NumberFormatter(format);
formatter.setMinimum(Double.MIN_VALUE);
formatter.setMaximum(Double.MAX_VALUE);
formatter.setAllowsInvalid(false);
editor = new JFormattedTextField(formatter);
editor.setBorder(UIManager.getBorder("Tree.editorBorder"));
}
#Override
public Object getCellEditorValue() {
return editor.getValue();
}
#Override
public boolean isCellEditable(EventObject anEvent) {
if (anEvent instanceof MouseEvent) {
return ((MouseEvent)anEvent).getClickCount() >= clickCountToStart;
}
return true;
}
#Override
public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
#Override
public boolean stopCellEditing() {
fireEditingStopped();
return true;
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
if(value instanceof Double){
editor.setValue(value);
}
return editor;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if(value instanceof Double) {
Color background = isSelected ? UIManager.getColor("Table.selectionBackground") : UIManager.getColor("Table.background");
Color foreground = isSelected ? UIManager.getColor("Table.selectionForeground") : UIManager.getColor("Table.foreground");
Border border = hasFocus ? UIManager.getBorder("Table.focusCellHighlightBorder") : BorderFactory.createEmptyBorder();
renderer.setBackground(background);
renderer.setForeground(foreground);
renderer.setBorder(border);
renderer.setValue(value);
return renderer;
} else {
String message = String.format("Not supported for %1$1s class!", value.getClass());
throw new IllegalArgumentException(message);
}
}
}
Disclaimer: it may not work properly with Nimbus look and feel as UIManager properties are named different. I've tested it using Metal, Windows, Windows Classic and Motif.
Here is the code I've used to test it:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
public class Demo {
private void initGUI(){
DefaultTableModel model = new DefaultTableModel(new Object[]{"Item", "Price USD", "Price EUR"}, 0);
model.addRow(new Object[]{"Fender stratocaster", 1599.99d, 1176.46d});
model.addRow(new Object[]{"Gibson Les Paul", 1299.99d, 955.87d});
model.addRow(new Object[]{"Pual Reed Smith Standard 24", 1999.99d, 1470.58d});
JTable table = new JTable(model);
table.setPreferredScrollableViewportSize(new Dimension(500, 300));
TableColumn priceUSD = table.getColumn("Price USD");
priceUSD.setCellRenderer(new CurrencyEditor(Locale.US));
priceUSD.setCellEditor(new CurrencyEditor(Locale.US));
TableColumn priceEUR = table.getColumn("Price EUR");
priceEUR.setCellRenderer(new CurrencyEditor(Locale.GERMANY));
priceEUR.setCellEditor(new CurrencyEditor(Locale.GERMANY));
JPanel content = new JPanel(new BorderLayout());
content.add(new JScrollPane(table));
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(content);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Demo().initGUI();
}
});
}
}
Screenshot

Related

Repainting problem when scrolling nested table in Java 9+

I have a Swing UI element that uses nested JTables. That is, the TableCellRenderer of the outer table returns JTable objects as the Component to render for each cells. This code works great under Java 8. Using the exact same code under Java 9 or Java 10 causes the inner table to not be repainted properly when scrolling the table. The table IS repainted properly when the window is resized.
Edit:
I did some more digging and traced the problem to lines 1862:1877 in BasicTableUI.java - these lines were added in Java 9. If I make my own TableUI without these lines, the problem disappears. From debugging, it appears that the issue is that in my case rMin and rMax are equal and setting rMax = rMax - 1 makes rMax < rMin which is obviously is bad. The added code seems to be there to deal with issues printing a table. Not sure why nested tables makes this break.
See the SSCE below:
package testing.test_painting;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import java.awt.Component;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class InnerTable extends JTable {
public InnerTable(TableModel dm) {
super(dm);
}
}
class InnerTableModel extends AbstractTableModel {
#Override
public int getRowCount() {
return 500;
}
#Override
public int getColumnCount() {
return 10;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return rowIndex + "," + columnIndex;
}
}
class OuterTableModel extends AbstractTableModel {
private final List<JTable> termTables;
public OuterTableModel() {
this.termTables = Arrays.asList(new InnerTable(new InnerTableModel()));
}
#Override
public int getRowCount() {
return 1;
}
#Override
public int getColumnCount() {
return termTables.size();
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return termTables.get(columnIndex);
}
public JTable getTermTable(int modelColumn) {
return termTables.get(modelColumn);
}
}
class OuterTable extends JTable {
private final List<TableRenderer> renderers;
private class TableRenderer implements TableCellRenderer {
private final OuterTableModel tableModel;
public TableRenderer(OuterTableModel tableModel) {
this.tableModel = tableModel;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
int modelColumn = convertColumnIndexToModel(column);
JTable termTable = tableModel.getTermTable(modelColumn);
termTable.setVisible(true);
return termTable;
}
}
private final OuterTableModel tableModel;
public OuterTable(OuterTableModel tableModel) {
super(tableModel);
renderers = new ArrayList<>(tableModel.getColumnCount());
for (int i = 0; i < tableModel.getColumnCount(); i++) {
renderers.add(new TableRenderer(tableModel));
}
this.tableModel = tableModel;
setCellDimensions();
}
#Override
public void setTableHeader(JTableHeader tableHeader) {
tableHeader.setUI(new BasicTableHeaderUI());
super.setTableHeader(tableHeader);
}
private void setCellDimensions() {
Dimension preferredSize = tableModel.getTermTable(0).getPreferredSize();
if (getRowHeight() != preferredSize.height) {
setRowHeight(preferredSize.height);
}
TableColumnModel columnModel = getColumnModel();
for (int i = 0; i < columnModel.getColumnCount(); i++) {
TableColumn column = columnModel.getColumn(i);
column.setMinWidth(preferredSize.width);
column.setMaxWidth(preferredSize.width);
column.setPreferredWidth(preferredSize.width);
column.setWidth(preferredSize.width);
}
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
}
#Override
public TableCellRenderer getCellRenderer(int row, int column) {
return renderers.get(column);
}
}
public class TestNestedTable {
public static void main(String[] args) {
OuterTableModel mainTableModel = new OuterTableModel();
OuterTable mainTable = new OuterTable(mainTableModel);
JFrame jFrame = new JFrame();
JScrollPane scrollPane = new JScrollPane(mainTable);
scrollPane.getVerticalScrollBar().setUnitIncrement(10);
jFrame.getContentPane().add(scrollPane);
jFrame.pack();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
}
make my own TableUI without these lines, the problem disappears.
Or simpler, you might be able to use JViewport#SIMPLE_SCROLL_MODE:
JScrollPane scrollPane = new JScrollPane(mainTable);
scrollPane.getVerticalScrollBar().setUnitIncrement(10);
scrollPane.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
I know its and old question, but I had the same issue and maybe someone else with the same problem finds this helpful.
This is an JDK bug reported several times (so a lot of duplicates) but most information can be found in the following ticket:
https://bugs.openjdk.java.net/browse/JDK-8202702
There has been added these lines to the BasicTableUI:
// Do not decrement rMax if rMax becomes
// less than or equal to rMin
// else cells will not be painted
if (rMax - rMin > 1) {
rMax = rMax - 1;
}
(s. http://hg.openjdk.java.net/jdk/client/rev/3ba3d39b91c7
or
https://github.com/openjdk/jdk/commit/bb9fed1008dee377725dc669401c389da685f618#diff-ae77528c5d554a9870371e7075801adc4ecd8f992ff484804f164b692b388858)
It is fixed for JDK 12 onward. Unfortunaltey, it is not backported to JDK 11, so if you are forced to use this LTS version one can either do it like #MattWallace and use own TableUI or you can overwrite your Table extending JTable with
#Override
public int getSelectedRow()
{
final int i = super.getSelectedRow();
return i == -1 ? -2 : i;
}
This might bring the problems back mentioned in BasicTableUI comments:
// We did rMax-1 to paint the same number of rows that are drawn on console
// otherwise 1 extra row is printed per page than that are displayed
// when there is no scrollPane and we do printing of table
// but not when rmax is already pointing to index of last row
// and if there is any selected rows
But if you can live with that, this can be also a workaround. Other workarounds like wrapping the Table in a JViewPort or a JScrollPane I cannot recommend as I got size issues with tables which can have changing row heights due to long texts.

JTable, how can I put icons in the header?

i'm trying to render some icons in a JTable header without success. I've already tried something like this: http://www.java2s.com/Tutorial/Java/0240__Swing/CustomizingColumnHeaderswithIcons.htm
but it doesn't show any icon in the header. How can I reach this goal?
Your link is good code , but in fact when you load a picture using absolute path or relative path , this code is not portable .So it is better to load picture from a class. Levels :
1) Create a empty class with this name : ImageResources
2) Please copy your picture
3) Please find ImageResources class in package explorer (in eclipse) and do right click on it (ImageResources) and press "Paste" menuItem.
4) Finally you need a small change in your code like this
ImageIcon blueIcon = new ImageIcon(ImageResources.class.getResource(yourPictureName));
It is working :)
Below method worked for me.
MyTable.getColumn("ColumnName").setHeaderRenderer(new TableCellRenderer() {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
return new JLabel(new ImageIcon(getClass().getResource("/project/img/ImgName.png")));
}
});
The question is a bit older, but I was only now faced with the same question.
Another approach is to override the TableModel's getColumnName function. HTML formatted text is supported.
Here is the sample code:
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
public class TableColumnHeaderIcon {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
TableModel tableModel = new AbstractTableModel() {
#Override
public String getColumnName(int columnIndex) {
// Build the html formatted text
StringBuilder sb = new StringBuilder();
sb.append("<html>");
sb.append("<img src=\"https://via.placeholder.com/16/007AAE/\">");
sb.append(" Column Title");
sb.append("</html>");
return sb.toString();
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public int getRowCount() {
return 0; // No data required
}
#Override
public Object getValueAt(int i, int i1) {
return null; // No data required
}
};
// Setup UI
JScrollPane scrollPaneTable = new JScrollPane();
JTable table = new JTable(tableModel);
scrollPaneTable.setViewportView(table);
JFrame mdi = new JFrame("Test JTable Column Header with Icon");
mdi.setSize(600, 400);
mdi.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
mdi.add(scrollPaneTable);
mdi.setVisible(true);
});
}
}

JTable - how do i insert a new JLabel for each row in a specific column?

im been looking around on the internet for about 5 hours now to debug on this issue ive been having. and Basically i haven't been able to find anywhere where a person tries to add a new JLabel for each row in a specific column.
functionality explanation: i get a file url, i take the postFix which is usually xlsx or
doc - this postfix i want to display in a JLabel in coalition with a Excel or Doc
Icon -.- but what my current code does is just paint the same JLabel over and over again because it only sets the CellRenderer one time and uses it on all the rows dispite the fact that im setting it in a for-loop for each iteration - all my code is correct for this functionality up until the renderer only gets called once.
so my question goes as follows - how do i add a new JLabel for each row
in a Column ? -
my code follows.
my TableCellRenderer:
public class JLabelRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = -166379583761969293L;
// private String fileExtension;
// private JLabel label;
private LogEntry log;
private JLabel label;
public JLabelRenderer(LogEntry log) {
label = new JLabel();
System.out.println("makeing a new JLabelRenderer");
this.log = log;
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
label = (JLabel) super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
System.out.println(log.getFileExtension());
if (log.getFileExtension().equalsIgnoreCase("xlsx")) {
label.setIcon((ImageIcon) Pictures.getXlsx());
label.setText(log.getFileExtension());
} else if (log.getFileExtension().equalsIgnoreCase("doc")) {
label.setIcon((ImageIcon) Pictures.getDoc());
label.setText(log.getFileExtension());
} else if (log.getFileExtension().equalsIgnoreCase("docx")) {
label.setIcon((ImageIcon) Pictures.getDoc());
label.setText(log.getFileExtension());
} else if (log.getFileExtension().equalsIgnoreCase("pdf")) {
label.setIcon((ImageIcon) Pictures.getPdf());
label.setText(log.getFileExtension());
}
value = label;
return label;
}
#Override
public void setHorizontalAlignment(int alignment) {
super.setHorizontalAlignment(alignment);
}
public void setLog(LogEntry log) {
this.log = log;
}
}
where i make my model:
(i have alot more code adding actionListeners to right-click functionality and what not
but thats hardly relevant)
public void makeLogModel()
{
logModel = new DefaultTableModel();
//addCellEditorListener(this);
logModel.setColumnIdentifiers(new String[]{"Lavet Dato", "Lavet Af", "Beskrivelse", "Sidst Redigeret Dato", "Sidst Redigeret Af", "Fil Type"});
setAutoCreateRowSorter(true);//allows to sort through the information.
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
setModel(logModel);
}
Where the magic was suppose to happen(update the log table after a search on logs)
public void updateLogTable(ArrayList<LogEntry> entryList)
{
logModel.setRowCount(entryList.size());
for(int i = 0; i < logModel.getRowCount(); i++)
{
setRowHeight(i, 30);
}
int row = 0;
for(LogEntry log : entryList)
{
logModel.setValueAt(log.getCreateDate(), row, 0);
logModel.setValueAt(log.getMadeBy(), row, 1);
logModel.setValueAt(log.getDescription(), row, 2);
logModel.setValueAt(log.getLastEdited(), row, 3);
logModel.setValueAt(log.getLastEditedBy(), row, 4);
labelRenderer = new JLabelRenderer(log);
getColumn("Fil Type").setCellRenderer(labelRenderer);
logModel.setValueAt(new JLabel(), logRow, 5);
row++;
}
}
i have read the Java documentation for components and Editors- but none of the
code examples are for a JLabel - probably because CellRenderer extends JLabel..
i have allso sniffed up the info that the 'value' parameter in the getTableCellRendererComponent() method is the one that is supposed to be set dynamically somehow... any surgestions would be greatly apriciatet, feel free to ask any questions.
thanks
logModel.setValueAt(new JLabel(), logRow, 5);
don't put JLabel, any JComponent to the model, XxxTableModel is designated to nest value for Renderer or Editor only, more in Oracle tutorial How to use Tables - Creating a Table Model
Renderer or Editor visually represents real JComponents, more in Oracle tutorial How to use Tables - Concepts: Editors and Renderers
label.setIcon((ImageIcon) Pictures.getXlsx());
Icon should be placed into local variables or array, list, whatever of Icons, don't load any FileIO from Renderer, renderer can be called many times per one second, e.g. from all mouse, keys and methods inplemented in APIs
Seems your problem in DefaultTableCellRenderer implementation.
You create renderer like JLabelRenderer(LogEntry log) because of, for all cells you have one instance of LogEntry, and log.getFileExtension() in getTableCellRendererComponent() returns same result for all rows.
In case of TableCellRenderer you need to use value parametr from getTableCellRendererComponent() method to determine extension and LogEntry instance.
Examine Concepts: Editors and Renderers.
Here is simple example for you, I use color instead of icons:
import java.awt.Color;
import java.awt.Component;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
public class TestFrame extends JFrame{
public TestFrame(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
init();
pack();
setVisible(true);
}
private void init() {
JTable table = new JTable(new Object[][]{
{1,"doc"},
{2,"xlsx"},
{3,"abc"}
},new Object[]{"nmb","extension"});
table.getColumnModel().getColumn(1).setCellRenderer(getRenderer());
add(new JScrollPane(table));
}
private TableCellRenderer getRenderer() {
return new 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("doc".equals(value)){
tableCellRendererComponent.setBackground(Color.YELLOW);
} else if("xlsx".equals(value)){
tableCellRendererComponent.setBackground(Color.GREEN);
} else {
tableCellRendererComponent.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
}
return tableCellRendererComponent;
}
};
}
public static void main(String... strings) {
new TestFrame();
}
}
Also don't put Component's to TableModel like here logModel.setValueAt(new JLabel(), logRow, 5); just value.

JTable: How to update cell using custom editor by pop-up input dialog box?

I am learning from this oracle tutorial that uses TableDialogEditDemo.java example class
I wrote a custom cell Renderer and Editor for JTable.
I register them to this Oracle TableDialogEditDemo.java class
...
...
//Set up renderer and editor for the Favorite Color column.
table.setDefaultRenderer(Color.class,
new ColorRenderer(true));
table.setDefaultEditor(Color.class,
new ColorEditor());
TableColumn c = table.getColumnModel().getColumn(2);
c.setCellRenderer(new CellStringRenderer()); //My custom Renderer
c.setCellEditor(new CellStringEditor()); // My custom Editor
//Add the scroll pane to this panel.
add(scrollPane);
...
...
(Updated description)
When I click on a cell an input dialogue box pops up and that is OK, and when I type a text and click "OK" the cell in the JTable is updated, but text is not displayed/rendered correctly, I have to click on any other cell to make the text content displayed correctly in the cell.
What is the wrong with my code?.
My Renderer
import java.awt.Component;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
public class CellStringRenderer extends JLabel implements TableCellRenderer
{
public CellStringRenderer()
{
this.setOpaque(true);
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
String stringValue = (String) value;
this.setText(stringValue);
return this;
}
}
My Editor (Updated)
import java.awt.Component;
import javax.swing.*;
import javax.swing.table.TableCellEditor;
public class CellStringEditor extends AbstractCellEditor
implements TableCellEditor
{
String input;
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
{
if (isSelected)
{
JOptionPane dialog = new JOptionPane();
input = dialog.showInputDialog(null, "new value");
return dialog;
}
return null;
}
#Override
public Object getCellEditorValue()
{
return input;
}
}
I am The author of this question, and I solved it. I will provide the solution so that others can get help from it.
I was trying to write a custom renderer and a custom editor to use with JTable.
The renderer simply uses JLabel to display data. It is already the standard component for JTable.
The editor is a dialogue box that appears when clicking on the cell that I want to edit.
Here is the solution:
The classes that will remain unchanged are the three classes from Oracle + my custom renderer class
1.TableDialogEditDemo.java.
2.ColorEditor.java
3.ColorRenderer.java
4.CellStringRenderer class provided above in the question body (my class)
The class that will be updated is the custom editor class (I changed its name from "CellStringEditor" to "DialogStringEditor"
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
public class DialogStringEditor extends AbstractCellEditor
implements TableCellEditor,
ActionListener
{
String newInput;
String oldValue;
JButton button;
static final String EDIT = "edit";
public DialogStringEditor()
{
button = new JButton();
button.setBackground(Color.WHITE);
button.setActionCommand(EDIT);
button.addActionListener(this);
button.setBorderPainted(false);
}
#Override
public void actionPerformed(ActionEvent e)
{
if (EDIT.equals(e.getActionCommand()))
{
newInput = JOptionPane.showInputDialog("Edit", oldValue);
if (newInput == null)
{
newInput = oldValue;
}
fireEditingStopped();
}
}
#Override
public Object getCellEditorValue()
{
return newInput;
}
#Override
public Component getTableCellEditorComponent(JTable table,
Object value,
boolean isSelected,
int row,
int column)
{
newInput = (String) value;
oldValue = (String) value;
return button;
}
}
This will work ok
You can also make a little update for the renderer class "CellStringRenderer" to control how are selction and unselection colors of the cell appear.
update this method:
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
String stringValue = (String) value;
this.setText(stringValue);
if (isSelected)
{
this.setBackground(table.getSelectionBackground());
this.setForeground(table.getSelectionForeground());
} else
{
this.setBackground(table.getBackground());
this.setForeground(table.getForeground());
}
return this;
}
}
Regards.

how to set JTable header background color when a cell is selected

I can successfully set the background color of the entire JTable header.
generally, this looks like this:
m_table.getTableHeader().setBackground(new Color(205,209,235));
where i am getting stuck is when a cell is selected, I have a ListSelectionListener...
I want to set the background of the currently selected columns in the header to another color. (not the background of the current cell, which Is pretty easy)
update:
i created this:
import java.awt.Color;
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import com.ee.common.StringUtil;
public class myHeaderRenderer extends DefaultTableCellRenderer
{
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if( table != null )
{
if ( isSelected ) {
table.getTableHeader().setBackground(Color.orange);
}
else {
table.getTableHeader().setBackground(new Color(205,209,235));
}
}
return null;
}
}
and i try to set it up like this:
class WorksheetTable extends JTable implements EEObjectSource, EEObjectTarget {
public WorksheetTable(WorksheetTableModel model) {
super(model);
setTableHeader(new EditableJTableHeader(getColumnModel()));
}
#Override
public void createDefaultColumnsFromModel() {
super.createDefaultColumnsFromModel();
setTableHeader(new EditableJTableHeader(getColumnModel()));
getTableHeader().setToolTipText("Double Click to Edit, Right Click for more options");
for (int i=0; i<getColumnCount(); i++) {
getColumnModel().getColumn(i).setPreferredWidth(150);
getColumnModel().getColumn(i).setWidth(150);
getColumnModel().getColumn(i).setHeaderRenderer(new myHeaderRenderer());
}
i get Runtime null pointers when the table first paints... missing something silly..
You can go on a JTable:
jTable.getColumModel().getColumn(columnIndex).setHeaderRenderer()
And you can supply you own cell renderer to render headers.

Categories