I ahve a JTable that is supposed to be 2 columns (String, JComboBox). When i initialize the table everything looks good. As soon as a I select a value in the table the JComboBox cell aquires the data type of the selected item.
I want to keep the JCOmboBox there and have it fire the events of data change and the Table ignore data changes in that column and keep the ComboBox populated.
My table has this as an override
#Override
public TableCellEditor getCellEditor(int row, int column) {
Object value = super.getValueAt(row, column);
if (value != null) {
if (value instanceof JComboBox) {
return new DefaultCellEditor((JComboBox) value);
}
return getDefaultEditor(value.getClass());
}
return super.getCellEditor(row, column);
}
Implementation
JComboBox uploadBox = new JComboBox();
uploadBox.addItem(MyPanel.UPLOAD_OPTIONS.PROMPT);
uploadBox.addItem(MyPanel.UPLOAD_OPTIONS.UPLOAD);
uploadBox.addItem(MyPanel.UPLOAD_OPTIONS.DONT_UPLOAD);
Object[][] tableData = new Object[][]{
{"Upload data on save", uploadBox}
};
table.setModel(
new DefaultTableModel(tableData, new String[]{"Description", "Options"}) {
Class[] types = new Class[]{String.class, JComboBox.class};
boolean[] canEdit = new boolean[]{false, true};
#Override
public Class getColumnClass(int columnIndex) {
return types[columnIndex];
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return canEdit[columnIndex];
}
});
table.getColumnModel().getColumn(1).setCellRenderer(new TableCellRenderer() {
#Override
public Component getTableCellRendererComponent(JTable jtable, Object o, boolean bln, boolean bln1, int i, int i1) {
return (Component)o;
}
});
answer is quite simple don't put JComboBox to the XxxTableModel or to set getColumClass for JComboBox.class, this is wrong (sure is possible but with bunch of side effects), XxxTableModel (is designated for) can hold directly only standard Java data types (String, Date, Icon/ImageIcon, Integer, Double etc... )
XxxTableModel should be store (if you don't want to parsing between Java data types) the same data type like as is stored in DefaultComboBoxModel (noting clear what constans are MyPanel.XXX), e.g in XxxTableModel is stored String value when DefaultComboBoxModel has the same data types, similair logics for Date, Icon/ImageIcon, Integer or Double
for more info to read Oracle tutorial How to use Tables - Using a Combo Box as an Editor
Related
I created a class inheriting from AbstractTableModel. I want to override setValueAt(), so that it will change the value of the cell in row r and column c into the multidimensional array v. But I keep getting errors.
public class ItemListTableModel extends AbstractTableModel{
public void setValueAt(Object v, int r, int c) {
rowData[r][c] = v;// This is where the error is.
fireTableCellUpdated(r, c);
}
#Override
public int getRowCount() {
}
#Override
public int getColumnCount() {
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
}
public boolean isCellEditable(int row, int col){
return true;
}
}
AbstractTableModel has no concept of the content of the model or how it's managed, that's kind of the point. It's designed to allow developers to design their own models based on their own complex requirements.
A simpler solution would be to use a DefaultTableModel, which provides all the functionality out of the box...
DefaultTableModel model = new DefaultTableModel(rows, columns); // you need to define rows and columns for yourself
model.setValueAt(row, column, value); // Again, you need to define the variables for your self
Should you "absolutely" need a custom table model based on AbstractTableModel, then you will need to provide the storage mechanisms which are used to store data within a given row/data yourself.
Typically, I define a POJO which represents the row and then add these to some kind of List, as it provides a simple mechanism for growing and shrinking the model
In your class you haven't declared the type of rowData. Presumably it's an 2 x 2 int array, but you haven't declared it, so the compiler cannot find the identifier. What you want to do is first declare rowData like
int[][] rowData = new int[r][v];
Then you can assign v to rowData as normal.
Example use of AbstractTableModel::setValueAt().
class MyTableModel extends AbstractTableModel {
private final String[] columnNames = new String[]{"Col One", "Col Two"};
private final Object[][] data = new String[][]{new String[]{"R1C1", "R1C2"}, new String[]{"R2C1", "R2C2"}};
#Override
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
......
rest of the code
In this code, I'm adding the data from Database. I want to set cell renderer with a label. But if I run this code I got only check box.
try {
List<Group> listgrChild = grMgmtModel.performList();
for (final Group group : listgrChild) {
table.getColumnModel().getColumn(0)
.setCellRenderer(new TableCellRenderer() {
// the method gives the component like whome the
// cell must
// be rendered
public Component getTableCellRendererComponent(
JTable table, Object value,
boolean isSelected, boolean isFocused,
int row, int col) {
boolean marked = new Boolean(String
.valueOf(value));
JCheckBox rendererComponent = new JCheckBox();
if (marked) {
rendererComponent.setSelected(true);
}
return rendererComponent;
}
});
tbModel.addRow(new Object[] { group.getGroupName() });
}
You comment, "renderComponent.setText("Hello") is giving only last value."
Verify that your TableModel has individual storage for each row's check box state and label value. In this example, the class Value holds the relevant data:
private static class Value implements Comparable<Value> {
private Boolean selected;
private Double value;
…
}
The corresponding TableModel manages a List<Value> and the required renderer and editor use the data from each Value instance accordingly. As an aside, Value implements the Comparable interface for convenience in sorting.
I am trying to implement a JTable which will format cells in columns in ways depending on their type. I am implementing TableCellRenderer's `getTableCellRendererComponent()' method to achieve this.
The problem is that my getTableCellRendererComponent() method never seems to be called, as the test output in the code never appears in the console.
Here is the code for the renderer:
public class MenuSheetTableCellRenderer extends JLabel implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(
JTable table, Object value,
boolean isSelected, boolean hasFocus,
int row, int column){
JLabel label = new JLabel();
System.out.println("test");
if( value instanceof GregorianCalendar ){
System.out.println("test2");
GregorianCalendar timeGregorianCalendar = (GregorianCalendar) value;
Date time = timeGregorianCalendar.getTime();
SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm a");
String mealTime = timeFormat.format(time);
label.setText(mealTime);
}
else if( value instanceof MealChoice){
label.setText(value.toString());
}
else if( value instanceof Recipe){
label.setText(" " + value.toString());
}
if (value instanceof String || value instanceof MealChoice){
label.setFont(new Font("Tahoma", Font.BOLD, 11));
}
return label;
}
}
Here is the code (edited for relevant code) which generates the table. Note that getRows() is my custom method that returns the data to be rendered and also that I am using netbeans (hence the abridged initComponents() method). Should be irrelevant.
private void initComponents() {
menuSheetTable = new javax.swing.JTable();
menuSheetTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
},
new String [] {
}
));
menuSheetTable.setEnabled(false);
menuSheetTable.setFocusable(false);
menuSheetTable.setShowHorizontalLines(false);
menuSheetTable.setShowVerticalLines(false);
jScrollPane1.setViewportView(menuSheetTable);
}
public void renderTable(){
String[] columns = { "Sunday", "Monday","Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
Object[][] rows = getRows();
DefaultTableModel menuSheetModel = new DefaultTableModel( rows, columns );
menuSheetTable.setModel( menuSheetModel );
MenuSheetTableCellRenderer renderer = new MenuSheetTableCellRenderer();
try{
menuSheetTable.setDefaultRenderer(Class.forName("java.lang.String"), renderer);
menuSheetTable.setDefaultRenderer(Class.forName("java.util.GregorianCalendar"), renderer);
menuSheetTable.setDefaultRenderer(Class.forName("MenuSystemManager.MealChoice"), renderer);
menuSheetTable.setDefaultRenderer(Class.forName("MenuSystemManager.Recipe"), renderer);
}
catch( ClassNotFoundException e){
System.exit(1);
}
}
The code which set the default renderers doesn't have the Object object registered. Because the rows[][] was declared to hold type "Object," the renderer was never called because it was passed Object objects, not GregorianCalendars, Strings, or Recipes.
I ran into the same issue. Well, you have already found the reason why it behaves like this. So, here is the solution.
JTable works with TableModel interface that supplies table with all information about the data it holds. This information also contains the class associated with a column. So basically, what you need to do is to derive TableModel or AbstractTableModel with your class which will hold the data for the table and override the method public Class<?> getColumnClass(int columnIndex).
After that enhancement you can use your custom TableCellRenderer.
In your code you supplied JTable with a DefaultTableModel which returns Object for any column and that's why it did not work out.
I have a sample code that we use to dynamic row numbers in Java Swing Table i.e JTable. I new to JavaFX and would like to the same in JavaFX. Is there is any way to set automatic row numbers in JavaFX Table
class LineNumberTable extends JTable {
private JTable mainTable;
public LineNumberTable(JTable table) {
super();
mainTable = table;
setAutoCreateColumnsFromModel(false);
setModel(mainTable.getModel());
setAutoscrolls(false);
addColumn(new TableColumn());
getColumnModel().getColumn(0).setCellRenderer(mainTable.getTableHeader().getDefaultRenderer());
getColumnModel().getColumn(0).setPreferredWidth(40);
setPreferredScrollableViewportSize(getPreferredSize());
}
#Override
public boolean isCellEditable(int row, int col) {
if (col == uneditableColumn) {
return false;
}
return bEdit;
}
#Override
public Object getValueAt(int row, int column) {
return Integer.valueOf(row + 1);
}
#Override
public int getRowHeight(int row) {
return mainTable.getRowHeight();
}
}
In JavaFX, you use TableColumns with CellFactories and CellValueFactories to populate your TableView.
The JavaFX tutorials have an article that might get you started.
In one approach I have used I convert the business objects to display into presentation objects and add all necessary properties (like in your case, the number) to them.
EDIT: In a second, cleaner approach, you could set your CellFactory to create a TableCell that shows its own index property in TableCell#updateItem(S, boolean):
public class NumberedCell extends TableCell{
protected void updateItem(Object object, boolean selected){
setText(String.valueOf(getIndex());
}
}
I created a Java GUI that displays the table using the following syntax:
table = new JTable(new MyTableModel(columnNames,
updateTable(cmbAdversary.getSelectedItem().toString(),
cmbdataType.getSelectedItem().toString())));
where columnNames is a Vector of Strings
cmbadversary and smbdataType are the selection od combo boxes.
and updateTable is a method that returns a Vector of Vectors depending on the combo box selection as follows:
static Vector updateTable(String FilterVal1 , String FilterVal2)
{
try {
myVector = tssc.testSeverityFunctionService(FilterVal1,FilterVal2);
} catch (Exception e) {
e.printStackTrace();}
return myVector;
}
This is how my custom class MyTableModel that extends AbstractTableModel looks like:
class MyTableModel extends AbstractTableModel
{
Vector columnNames = new Vector();
Vector Fdb = new Vector();
public MyTableModel(Vector cName,Vector rName){
this.columnNames = cName;
this.Fdb = rName;}
public int getColumnCount() { // number of columns in the model.
return columnNames.size();
}
public int getRowCount() { // number of rows in the model.
return Fdb.size();
}
#Override
public String getColumnName(int col) {
return columnNames.get(col).toString();
}
public Object getValueAt(int row, int col) {
Vector v = (Vector) this.Fdb.get(row);
return v.get(col);
}
#Override
public Class getColumnClass(int c) {
Vector v = (Vector) Fdb.get(0);
return v.get(c).getClass();}
public boolean isCellEditable(int row, int col)
{ return true; }
public void setValueAt(Vector value, int row, int col)
{
for(int i=0;i<value.size();i++)
{ for(int j=0;j<columnNames.size();j++) {
Fdb.setElementAt(value.get(j),j); }
}
fireTableCellUpdated(row, col);
}
}
The problem is that when I run the code, the table GUI show me initial values but fails to update when I change the selection in the 2 comboboxes and click the selection button.
The Selection button, btw, calls a method which implements the action listener.
Please help me out. Am no pro in Java, but willing to learn. If you have any followup qs., I'll be happy to provide details.
Your solution seems overly complicated. If I understand the basics, the user chooses a value from a combo box, then based on the selection some data is loaded into the table.
There is no need to create a custom table model to do this.
A TableModel contains data. If you want to change the data, then one way to do this is to simply create a new TableModel. So you add an ActionListener to your combo box. When an item is selected you retrive your data and load the data into an Vector or an Array. Using this data you can create a new TableModel and update the JTable in two lines of code:
DefaultTableModel model = new DefaultTableModel(...);
table.setModel( model );
If you need to customize the model to override the getColumnClass() or isCellEditable() methods, then you should extend the DefaultTableModel. I don't see any need to implement the whole model.