This summer is not a real summer, i thought.
A cup of coffee make me feel it as a summer, tough (lol).
I have a little bit naughty JTable. OMG.
Below is my JTable that use my own customized TableModel. You could see it on its method of getColumnClass(), there... it was made for returning as JLabel only.
ANd then, I also customize the DefaultRenderer.
jtbl_inGameEasy = new javax.swing.JTable();
jtbl_inGameEasy.setFont(new java.awt.Font("squeaky chalk sound", 0, 14)); // NOI18N
jtbl_inGameEasy.setForeground(new java.awt.Color(255, 255, 255));
jtbl_inGameEasy.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
},
new String [] {
"null", "null", "null", "null", "null"
}
) {
boolean[] canEdit = new boolean [] {
false, false, false, false, false
};
public boolean isCellEditable(int rowIndex, int columnIndex) {
return canEdit [columnIndex];
}
public Class getColumnClass(int columnIndex) {
return JLabel.class;
}
});
jtbl_inGameEasy.setDefaultRenderer(JLabel.class, new JLabelTableRenderer());
jtbl_inGameEasy.setFocusable(false);
jtbl_inGameEasy.setOpaque(false);
jtbl_inGameEasy.setRowHeight(55);
jtbl_inGameEasy.setShowHorizontalLines(false);
jtbl_inGameEasy.setShowVerticalLines(false);
jtbl_inGameEasy.setTableHeader(null);
jtbl_inGameEasy.addMouseListener(new java.awt.event.MouseAdapter() {
public void mousePressed(java.awt.event.MouseEvent evt) {
jtbl_inGameEasyMousePressed(evt);
}
});
And Where is the JTableRenderer? Here...
this is my Custom Renderer below...
public class JLabelTableRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
if (value instanceof JLabel) {
//This time return only the JLabel without icon
return (JLabel) value;
} else {
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
}
And then, I need to pu JLabel inside each of the Cell.
I start to add some JLabel(s) inside the Object of Array over the Cells of the JTable using this code; (which is no problem).
DefaultTableModel o_dtb = jtbl_inGameEasy.getModel();
o_dtb.addRow(myArrayCustomizedObjectofJLabel);
Everything works fine, I guess. But because of My purpose is that ---> To put an Icon or to make it Invisible once the user Click on JTable Cells, thus I I tried to do my MouseEvent once pressed and calling these lines of code;
private void jtbl_inGameEasyMousePressed(java.awt.event.MouseEvent evt) {
// Checking inGameEasy Table Ans
javax.swing.JTable source = (javax.swing.JTable) evt.getSource();
int row = source.rowAtPoint(evt.getPoint());
int column = source.columnAtPoint(evt.getPoint());
DefaultTableModel o_dtb = (DefaultTableModel) jtbl_inGameEasy.getModel();
String s_questAns = "" + Game.getCurrentQuestion().getResult();
String s_userAns = "" + o_dtb.getValueAt(row, column);
// These two lines below are not Working, why yaa??
((JLabel) o_dtb.getValueAt(row, column)).setVisible(false);
((JLabel) o_dtb.getValueAt(row, column)).setIcon(myIcon);
if (s_questAns.equals(s_userAns)) {
Game.correct();
System.err.println("nice ans!");
} else {
jll_txtMiss.setText("Miss : " + Game.wrong());
System.err.println("nope!");
}
nextQuestion();
}
And seems to me that the Marked (below the commented) code above are not working.
Yeah, the JLabel casted couldn't be changed its Icon (image), and nor its visibility as well. Is it the Model cause all of this?
NB: My Cells data added later after the Model created differently.
It seems to me that what you want to do is to just leave all table cells empty, except the ones clicked by the user, where some mark icon should appear, right?
If so, your table model should not contain JLabel instances. A JLabel is a visual component, used to render some data graphically. The data itself is not a JLabel. In your case, I think it should thus be a Boolean (true when clicked by the user, false otherwise).
Now you could use a custom renderer (although the default one for booleans could also be OK) to display your booleans. This renderer would be a subclass of DefaultTableCellRenderer, which is itself a subclass of JLabel. The renderer (the same instance for all the cells configured to use this renderer) would just display a marked icon if the Boolean value to render is true, and a not marked icon (or no icon at all) when the Boolean value to render is false.
Then, your click handler would just have one mission: make the clicked cell contain true rather than false. To do that, it would just have to change the appropriate value in the table model.
To recap: a table model is used to hold data. Think of it as the data you would find in a database. Would you hold a JLabel in the database? No, you would hold a boolean, a string or an integer. The table can render this data as you want, and this is the mission of the renderer.
Side note: stop with the hungarian notation: it doesn't make sense in Java. It makes code hard to read. Everything except primitive types is an object, and you can't assign a meaningful prefix to every possible type. Rather, use readable and meaningful english names : tableModel rather than o_dtb, correctAnswer rather than s_questAns, userAnswer rather than s_userAns.
Related
I am a software developer apprentice and have to write a graphical project specific configuration editor for my company. I load the data from the configuration excel file of the project with Apache POI and wrap the data into ConfigValue Objects. For different ConfigValue objects there has to be different cell editors and renderers...
The GUI of my programm uses a custom JTable and DefaultTableModel. Every value in the table / model is a ConfigValue which should rendered differently for defined different ConfigTypes. (As far I got it all working - import, wrapping, load into table)
But I have some problems with the TableCellRenderer or TableCellEditor of one of the custom types which should rendered as a ComboBox which contains all possible backend entity values. The ComboBox gets rendered and the correct beginning values are displayed... But when I change one cell to another ConfigValue... The renderer does not display this value... (it always changes to the same value (first of the editor's value) for a cell)
Can anyone help me out what I am doing wrong with my Editor/Renderer?
public class ConfComboBoxCellEditor extends DefaultCellEditor {
public ConfComboBoxCellEditor(List<ConfigValue> possibleValues) {
super(new JComboBox(possibleValues.toArray()));
}
#Override
public Object getCellEditorValue() {
Object cellEditorValue = super.getCellEditorValue();
System.out.println("DEBUG - CELL EDITOR - get editor value --> " + ((ConfigValue) cellEditorValue).toString());
return cellEditorValue;
}
}
public class ConfComboBoxCellRenderer extends JComboBox<ConfigValue> implements TableCellRenderer {
public ConfComboBoxCellRenderer() {
System.out.println("NEW CELL RENDERER");
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
ConfComboBoxCellRenderer renderer = (ConfComboBoxCellRenderer) table.getCellRenderer(row, column);
renderer.removeAllItems();
renderer.addItem((ConfigValue) value);
renderer.setSelectedItem(value);
System.out.println("DEBUG - CELL RENDERER " + row + ", " + column + " - get cell render comp --> " + ((ConfigValue) value));
return this;
}
}
Can anyone help me out what I am doing wrong with my Editor/Renderer?
JTable support JComboBox as TableCellEditor, there isn't any issue to set different dataset for each of JComboBoxes used as TableCellEditor
TableCellRenderer only shows, painting the value stored in DefaultTableModel, then every code lines inside renderer.xxxXxx are missinterpreting of Renderers Concept in Swing, contraproductive and could be heavy tasks, Renderer isn't place to set/getValue, a new event is fired from all mouse/key events from all cell are visible in JViewport, plus internal events from JTable/TableModel APIs,
your Renderer isn't about how to painting JComboBox as rendering component
nothing cleaver, nor specifics without an SSCCE/MCVE, short runnable, compilable with hardcoded value for JTable/DefaultTableModel in local variable
We are using a JTable which displays data along with Status (New, Processed, Closed). Each status row has a different color which is achieved by overloading prepareRenderer() of JTable.
Now we need to sort that table and we are using table.setAutoCreateRowSorter(true); to achieve that. The rows get sorted properly, but the color of rows remains the same. We need to reapply the color to all the rows after this operation based on the status column.
I was wondering what could be the best way to achieve that. There are several ways I can think of:
Repaint/Revalidate the table. But simply doing this would not work I think.
Capture mouseClicked event and identify whether column header was clicked then call prepareRenderer() manually and then call repaint/revalidate
Then I read one of the questions here wherein one of the answers was mentioned not to call repaint/revalidate directly, rather change the underlying data model and it will automatically call the above methods.
I don't know how to go about it. Can anyone please provide an insight into what is the correct way to achieve this?
For change cell color in JTable with setAutoCreateRowSorter(true) I used method table.getRowSorter().convertRowIndexToModel(row) in my TableCellRenderer
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
import java.awt.*;
public class OwnTableCellRenderer extends DefaultTableCellRenderer {
public OwnTableCellRenderer() {
super();
setOpaque(true);
}
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
setBackground(Color.white);
setForeground(Color.black);
TableModel model = table.getModel();
int modelRow = table.getRowSorter().convertRowIndexToModel(row);
int columnStatusPosition = 5;
String statusColumnValue = (String) model.getValueAt(modelRow, columnStatusPosition);
if (statusColumnValue.equals("ACTIVE")) {
if (isSelected) {
setBackground(Color.green);
} else {
setBackground(Color.yellow);
}
}
setText(value != null ? value.toString() : "");
return this;
}
}
And then
table.setDefaultRenderer(Object.class, new OwnTableCellRenderer());
I am creating my first JTable that requires me to create a custom AbstractTableModel, TableCellEditor, and DefaultTableCellRenderer. Given that I have not needed to create these before, I've made some significant progress in getting my table to behave as desired.
However, I am getting overwhelmed with all the different methods I am overriding, and am spinning my wheels trying to figure out how to modify the ImageIcon of a particular cell. The cell must contain a JLabel, as it needs both an ImageIcon as well as a text string. I can already set the initial ImageIcon (although I am probably doing it incorrectly), but I can't set an updated ImageIcon. Nothing fails, but no change is made.
In a general sense, what is the best way to get and set an icon to a JLabel cell of a JTable, assuming all of these models, editors, and renderers have already been instantiated?
My model has already been defined to return JLabel.class for these cells, if you're wondering, and I also do a fireTableCellUpdated(row, col) once the change has supposedly been made. If I do a System.out.println(getIcon()) before and after the update, I can even see the source has changed.
Here is some of the code (updated with URL/ImageIcon fix in place):
class MonitorTable extends JTable {
MonitorTableModel model = new MonitorTableModel(rows, columnNames);
setModel(model);
...
public void setIconAt(ImageIcon icon, int row, int col) {
model.setIconAt(icon, row, col);
} // End setIconAt(ImageIcon, int, int)
...
class MonitorTableModel extends AbstractTableModel {
...
public void setIconAt(ImageIcon icon, int row, int col) {
StatusTableCellRenderer cell =
(StatusTableCellRenderer)getColumnModel().getColumn(col).getCellRenderer().
getTableCellRendererComponent(myTableObject, null, false, false, row, col);
System.out.println(cell.getIcon()); // Shows initial icon source
cell.setIcon(icon);
fireTableCellUpdated(row, col); // Should update the table
System.out.println(cell.getIcon()); // Shows new icon source
System.out.println("Cell updated");
} // End setIconAt(ImageIcon, int, int)
} // End class MonitorTableModel
public class StatusTableCellRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int col) {
setIcon(imgGray);
setText((String)value);
return this;
} // End getTableCellRendererComponent(JTable, Object, boolean, boolean, int, int)
} // End class StatusTableCellRenderer
} // End class MonitorTable
My model has already been defined to return JLabel.class for these cells,
But according to the code in your renderer you expect a String value in these cells:
setText((String)value);
I don't like your setIcon() method. I would not pass in the URL. I would pass in the Icon. Maybe you have a problem that the icon has not been read into memory at the time the cell is rendered.
what is the best way to get and set an icon to a JLabel cell of a JTable,
You should not store a JLable in the TableModel. It is expensive to store Swing components in the model, that is why Swing components use renderers. Instead you store a custom Object like "LabelInfo" which contains two properties, the text and the Icon. Then your custom renderer will extend the default renderer and invoke super.getTableCellRendererComponent(). You can then access your object and rest the text/icon properties of the renderer. You should not be creating objects in the renderer.
Now when you want to change something in the model you can do:
LabelInfo info = (LabelInfo)table.getValueAt(row, column);
info.setIcon(...);
table.setValueAt(info, row, column);
Thats all you need. There is not custom code to repaint the cell or anything because that is already built intothe setValueAt(...) method. of your table model.
Edit: a simple example for using a custom Object in the TableModel.
1) to add the object to the model you do something like:
LabelInfo info = new LabelInfo("some Text", yourIcon);
table.setValueAt(info, row, column);
2) the code for your custom renderer would be:
class LabelInfoRenderer 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);
LableInfo info = (LabelInfo)value;
setIcon( info.getIcon() );
return this;
}
}
Call the fireTableDataChanged from your model.
Try call the repaint method from JLabel too.
The better way to do it, is implement a CellRenderer, with returns a JPanel and make the draw method in the paintComponent. You can load a BufferedImage instead of a ImageIcon and use it to draw in the JPanel.
Try change you Renderer to:
public class StatusTableCellRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int col) {
JLabel comp = new JLabel(new ImageIcon(imgGray));
comp.setText((String)value);
return comp;
} // End getTableCellRendererComponent(JTable, Object, boolean, boolean, int, int)
}
You are doing some mess, this is not the right way to work with the TableModel, you should return the URL for the image in the getValueAt(int row, int col), after it, register the CellRenderer to correspond to cells with URL.class. The renderer is automatically called from the JTable, you don't need to extends JTable either, you have only to implement the Renderer and the Model.
The setIconAt should only call the setValueAt and put your URL at the column, the renderer take care of the rest.
I fixed this by changing setIcon(imgGray) to if (getIcon() == null) setIcon(imgGray);.
The issue is my getTableCellRendererComponent method was setting the icon to imgGray every time. Apparently my setIconAt method, which calls getTableCellRendererComponent, was being overridden, even though the "new" icon value was processed after the "old" value was (re)set.
I ended up removing all my setIcon methods and moved the relevant logic into my StatusTableCellRenderer class. That way I pass the value of the cell and let the renderer do the icon setting based on that value. It makes more sense this way, and works beautifully. I have confirmed that initial setting and all subsequent updates are performing as expected.
The logic of setting the icon is pretty simple -- set the predefined icon based on certain predefined threshold values.
double val;
if (getIcon() == null) setIcon(imgGray); // Initialize
if ((value == null) || (value == "")) {
val = 0;
} else {
val = Double.parseDouble(value.toString());
} // End if
if (val <= THRESHOLD1) {
setIcon(icon1);
} else if (val <= THRESHOLD2) {
setIcon(icon2);
...
} // End if
setText(value.toString());
I was very concerned about suggestions to make brand new objects to use, when the default JLabel was exactly what I needed. It was both unnecessary and a potential performance hit to the JTable. Thank you all for your insight and assistance. This was driving me batty!
i am making a twitter client (desktop application) in Java, i am using twitter4j API also. i have managed to do the search for tweets and i get back results and i show them in a Jlist.
what i want is that i want to show tweets nicely in the list, not only as a text .. show the image of the user, the tweet, tweeted by ... etc all this information .. in addition attach additional data like star rating .. how can i add that to a JList ? can the Jlist hold different objects .. Jpanels for example ..
Instead I suggest you put a set of JPanels inside a JScrollPane.
A JList's renderer must be a JComponent, so you can use any Swing object, including JPanels.
You can also use HTML in a JLabel if it is easier to do so than using a JPanel.
To use a custom renderer, you do something like this..
myList.setCellRenderer(new CustomRenderer());
and then create a renderer like this
public class CustomRenderer extends DefaultListCellRenderer {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) {
JPanel panel = new JPanel();
// set up the panel for your exact display requirements.
return(panel);
}
}
Suggest using a JTable, which has several columns, instead of a JList.
Also suggest using GlazedLists, which makes it really easy to display lists with fields in a JTable, so that they update automatically when the underlying list changes.
Here's an example of some code I wrote recently which displays something similar:
private void bindEmailTargetTable(NotificationModel newModel) {
JTable table = getUI(UIKey.EMAIL_TARGET_TABLE);
EventList<EmailTarget> displayList = newModel.getEmailTargets();
TableFormat<EmailTarget> tf = new TableFormat<EmailTarget>()
{
#Override public int getColumnCount() {
return 4;
}
private final String[] columns = { "address", "description", "msg left", "msg limit" };
#Override public String getColumnName(int col) {
return this.columns[col];
}
#Override public Object getColumnValue(EmailTarget item, int col) {
switch (col)
{
case 0:
return item.getAddress();
case 1:
return item.getDescription();
case 2:
return item.getRemainingMessages();
case 3:
return item.getMessageLimit();
default:
return "";
}
}
};
EventTableModel<EmailTarget> etm = new EventTableModel<EmailTarget>(displayList, tf);
table.setModel(etm);
}
That's 33 lines of code to take a JTable and make it automatically update itself to display 4 fields of each EmailTarget in an EventList<EmailTarget>.
For non-text field contents, you just need a custom TableCellRenderer.
AS Jason suggested its better to go for jtable instead of JLIst. In fact you can use any free Java based table classes that have extended functionality over JTables. JIDE is one such library but its commercial. you can search and find a lot..
The high level: I have a JTable that the user can use to edit data.
Whenever the user presses Enter or Tab to finish editing, the data is saved (I'm asusming that "saved" really means "the TableModel's setValueAt() method is called".)
If the user leaves the cell in any other way after making an edit, the new data is not saved and the value stays the way it was. So, for example, if the user changes a value and then clicks on some other widget on the screen, the change doesn't "stick."
I believe that this is the default behavior for a JTable full of Strings, yes?
For a variety of reasons, the desired behavior is for the cell to save any and all edits whenever the user leaves the cell. What's the best/right way to get Swing to do this?
Table Stop Editing explains whats happening and gives a couple simple solutions.
One of the simple solutions proposed
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
is good only for String columns. The problem is if I have, for example, Float type of the column being edited, enter an empty string in corresponding cell and then click on any other control of the window – Java throws NullPointerException in CellEditorRemover.propertyChange() method of JTable.java. It uses getCellEditor() call to stop or cancel editing but it returns null in this case. If the value entered is not empty or if I remove terminateEditOnFocusLost flag everything is fine. Probably, the situation described is a bug.
I hope I can provide a solution based on one of the previous posts. It’s not so trivial as I supposed before but seems to me it works.
I had to inherit my own cell editor from default cell editor and my own text field from JTextField which has FocusListener. This focus listener works fine when editing cell loses a focus, and a focus gained by another control of the window. But in the case of cell selection changes focus listener is “deaf”. That’s why I also have to remember previously valid value before editing start to restore it if the entered value will be invalid.
See the code below. Tested with Double, Float and Integer, but I hope this will also work with Byte and String.
Text field with focus listener:
public class TextFieldCell extends JTextField {
public TextFieldCell(JTable cellTable) {
super(); // calling parent constructor
final JTable table = cellTable; // this one is required to get cell editor and stop editing
this.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
}
// this function successfully provides cell editing stop
// on cell losts focus (but another cell doesn't gain focus)
public void focusLost(FocusEvent e) {
CellEditor cellEditor = table.getCellEditor();
if (cellEditor != null)
if (cellEditor.getCellEditorValue() != null)
cellEditor.stopCellEditing();
else
cellEditor.cancelCellEditing();
}
});
}
}
Default cell editor class:
class TextFieldCellEditor extends DefaultCellEditor {
TextFieldCell textField; // an instance of edit field
Class<?> columnClass; // specifies cell type class
Object valueObject; // for storing correct value before editing
public TextFieldCellEditor(TextFieldCell tf, Class<?> cc) {
super(tf);
textField = tf;
columnClass = cc;
valueObject = null;
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
TextFieldCell tf = (TextFieldCell)super.getTableCellEditorComponent(table, value, isSelected, row, column);
if (value != null) {
tf.setText(value.toString());
}
// we have to save current value to restore it on another cell selection
// if edited value couldn't be parsed to this cell's type
valueObject = value;
return tf;
}
#Override
public Object getCellEditorValue() {
try {
// converting edited value to specified cell's type
if (columnClass.equals(Double.class))
return Double.parseDouble(textField.getText());
else if (columnClass.equals(Float.class))
return Float.parseFloat(textField.getText());
else if (columnClass.equals(Integer.class))
return Integer.parseInt(textField.getText());
else if (columnClass.equals(Byte.class))
return Byte.parseByte(textField.getText());
else if (columnClass.equals(String.class))
return textField.getText();
}
catch (NumberFormatException ex) {
}
// this handles restoring cell's value on jumping to another cell
if (valueObject != null) {
if (valueObject instanceof Double)
return ((Double)valueObject).doubleValue();
else if (valueObject instanceof Float)
return ((Float)valueObject).floatValue();
else if (valueObject instanceof Integer)
return ((Integer)valueObject).intValue();
else if (valueObject instanceof Byte)
return ((Byte)valueObject).byteValue();
else if (valueObject instanceof String)
return (String)valueObject;
}
return null;
}
It the code of table initialization you have to add the following:
myTable.setDefaultEditor(Float.class, new TextFieldCellEditor(new TextFieldCell(myTable), Float.class));
myTable.setDefaultEditor(Double.class, new TextFieldCellEditor(new TextFieldCell(myTable), Double.class));
myTable.setDefaultEditor(Integer.class, new TextFieldCellEditor(new TextFieldCell(myTable), Integer.class));
Hope, this will help somebody who have the same problem.
You need to add a focus listener. Given that JTable is basically a container of its cell components, you actually want the focus listener for every cell in your table that needs to behave in the way you indicated.
To do this, you will need to create custom cell editor, which wraps the cell component that has a registered focus listener. And when you get the callback for the loss of focus event, you do the data save, as you require.
This pretty much details most of what you need to do. The details of implementing the focus listener is not there, but that is fairly straightforward.
Lets say you do use a JTextComponent as your cell component. Then:
public void focusLost(FocusEvent e) {
JTextComponent cell = (JTextComponent) e.getSource();
String data = cell.getText();
// TODO: save the data for this cell
}
[p.s. edit]:
The thread that is calling you with this event is the dispatch thread. Do NOT use it for actions with high latency. But if you are just flipping bits in the heap, it should be ok.