Adding a JButton into a JTable which uses abstract table model - java

I am trying to add a JButton as the first column on the table I created. I did make a research and couldn't find any solution for the tables that use abstract table model.
Here, I create an object array for each record that has texts and boolean variables to have the table render check boxes. Then those object arrays saved into an ArrayList
Here's my code to create my table data.
public ArrayList<Object[]> setTableData(){
/*
* ItemInfo fields
**********************
* line[0] - ReferenceNo
* line[1] - Quantity
* line[2] - ItemNameDescriptionSKU
* line[3] - Cube
*/
//Setting the data for the table
ArrayList<Object[]> itemList = new ArrayList<>();
for (int i=0; i<this.itemInfo.size();i++){
Object[] tempArray = new Object[7];
tempArray[0] = this.itemInfo.get(i)[1]; //Quantity
tempArray[1] = this.itemInfo.get(i)[2].toUpperCase(); //Item description
tempArray[2] = this.itemInfo.get(i)[3]; //Cube
//This adds charges if the payment type is COD
//To not to write the charge amount for every row
//checks the COD type only at the first record of items
if (i==0 && this.invoice[8].equals("COD"))
tempArray[3] = this.invoice[22]; //Charges if the invoice type is COD, null otherwise
else
tempArray[3] = " ";
tempArray[4] = new Boolean (false); //Loaded
tempArray[5] = new Boolean (false); //Delivered (Will be ignored if pickup)
itemList.add(tempArray);
}
return itemList;
Here's my table model
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;
public class TicketTableModel extends AbstractTableModel {
private ArrayList<Object[]> data;
private boolean isDelivery;
private String[] columns;
public TicketTableModel(ArrayList<Object[]> itemInfo, boolean isDelivery){
super();
this.data = itemInfo;
this.isDelivery = isDelivery;
}
#Override
public String getColumnName(int i) {
return this.columns[i];
}
public void setColumns ( String[] columns1 ){
this.columns = columns1;
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public int getColumnCount() {
return columns.length;
}
#Override
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
#Override
public boolean isCellEditable(int row, int col) {
if (col < 3)
return false;
else
return true;
}
#Override
public void setValueAt(Object value, int row, int col) {
data.get(row)[col] = value;
fireTableCellUpdated(row, col);
}
#Override
public Object getValueAt(int row, int col) {
return this.data.get(row)[col];
}

Take a look at Table Button Column.
This class implements the render/editor needed to make the button functional. You also provide the Action to invoke when the button is pressed.

The type of TableModel is irrelevant. If you want to "show" a button on a JTable, you supply a TableCellRenderer (and probably a TableCellEditor) for the column which is capable of rendering the button based on the values of the column it represents.
This will mean that your TableModel will need to support a column within it's model which represents the button.
Take a closer look at Using Custom Renderers, Using Other Editors and How to Use Tables

Related

How to stop adding already added value from JCombobox to JTable

I am working on netbeans and creating a swing application. I have created a JComboBox and a JTable. I am able to add value from JComboBox to JTable on a button click but if I repeat the same process the same value is again again added to the table. How to stop adding the existed value of JComboBox.
This is the code of JComboBox
private void populateCombo(){
organizationComboBox.removeAllItems();
for (Organization.Type type : Organization.Type.values()){
organizationComboBox.addItem(type);
}
}
This is the code of JTable
private void populateTable(){
DefaultTableModel model = (DefaultTableModel) organizationTable.getModel();
model.setRowCount(0);
for (Organization organization : organizationDirectory.getOrganizationList()){
Object[] row = new Object[2];
row[0] = organization.getOrganizationID();
row[1] = organization.getName();
model.addRow(row);
}
}
This is the code for my add button
Type type = (Type) organizationComboBox.getSelectedItem();
Organization o = organizationDirectory.createOrganization(type);
if(type.equals(Type.Receptionist)){
o.getSupportedRole().add(new ReceptionistRole());
}else
if(type.equals(Type.Doctor)){
o.getSupportedRole().add(new DoctorRole());
}else
if(type.equals(Type.VaccineManager)){
o.getSupportedRole().add(new VaccineManagerRole());
}else
if(type.equals(Type.LabAssistant)){
o.getSupportedRole().add(new LabAssistantRole());
}else
if(type.equals(Type.Donor)){
o.getSupportedRole().add(new DonorRole());
}else
if(type.equals(Type.Patient)){
o.getSupportedRole().add(new PatientRole());
}
populateTable();
Thanks in advance.
Don't use DefaultTableModel. This class is only for simple cases and demo applications. Simply look here for example of your own model.
So your model will looks like:
public class OrganizationModel extends AbstractTableModel {
protected String[] columnNames;
protected List<Organization> dataVector;
public OrganizationModel(String[] columnNames) {
this.columnNames = columnNames;
dataVector = new ArrayList<Organization>();
}
public String getColumnName(int column) {
return columnNames[column];
}
public boolean isCellEditable(int row, int column) {
return false;
}
public Class getColumnClass(int column) {
return String.class;
}
public Object getValueAt(int row, int column) {
return column == 0? dataVector.get(row).getOrganizationID() : dataVector.get(row).getName();
}
public void addRowWhenNotExist(Organization o) {
if (!dataVector.contains(o)) {
dataVector.add(o);
fireTableRowsInserted(dataVector.size() - 1, dataVector.size() - 1);
}
}
}
For correct working of this example you also need correct definition of methods equals and hashCode for your class Organization.
public class Organization {
// your stuff
public boolean equals(Object another) {
if (another instanceof Organization) {
return getOrganizationID() == ((Organization) another).getOrganizationID();
} else {
return false;
}
}
public int hashCode() {
return getOrganizationID();
}
}

Is there a way to put an ArrayList into a JTable where each line is the next index of the ArrayList?

I have many different array lists. I want each index to be a new row in the JTable but I'm not sure how to do that. I made a for loop but it is not working.
Is there even a way to populate a JTable with an array list and not an array?
public TableCreator() {
super(new GridLayout(1,0));
String[] columnNames = {"Item Type",
"Description",
"Size",
"Price"};
// for(int i=0; i<ShoppingFunctions.cartType.size(); i++){
for(int i=0; i<GUI.size.size(); i++){//FIX!!!!
item = ShoppingFunctions.cartType.get(i)+"\n";
described = GUI.describe[GUI.imageNum];
sizes = GUI.size.get(i);
price = ShoppingFunctions.cartPrice.get(i)+"\n";
}//end of for
Object[][] data = {{item, described, sizes, price}};
final JTable table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true);
if (DEBUG){
table.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e){
printDebugData(table);
}//end of method
});//end of listener
}//end of if
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}//end of method
The simple answer is to create your own, based on something like AbstractTableModel
For example...
public abstract class AbstractGenericTableModel<R> extends AbstractTableModel {
protected ArrayList<R> rows;
protected List columnIdentifiers;
public AbstractGenericTableModel() {
this(0, 0);
}
public AbstractGenericTableModel(int rowCount, int columnCount) {
this(new ArrayList(columnCount), rowCount);
}
public AbstractGenericTableModel(List columnNames, int rowCount) {
setData(new ArrayList<>(rowCount), columnNames);
}
public AbstractGenericTableModel(List<R> data, List columnNames) {
setData(data, columnNames);
}
public List<R> getRowData() {
return rows;
}
private List nonNullVector(List v) {
return (v != null) ? v : new ArrayList();
}
public void setData(List<R> data, List columnIdentifiers) {
this.rows = new ArrayList<>(nonNullVector(data));
this.columnIdentifiers = nonNullVector(columnIdentifiers);
fireTableStructureChanged();
}
public void addRow(R rowData) {
insertRow(getRowCount(), rowData);
}
public void insertRow(int row, R rowData) {
rows.add(row, rowData);
fireTableRowsInserted(row, row);
}
/**
* Removes the row at <code>row</code> from the model. Notification of the row being removed will be sent to all the listeners.
*
* #param row the row index of the row to be removed
* #exception ArrayIndexOutOfBoundsException if the row was invalid
*/
public void removeRow(int row) {
rows.remove(row);
fireTableRowsDeleted(row, row);
}
public void setColumnIdentifiers(List columnIdentifiers) {
setData(rows, columnIdentifiers);
}
#Override
public int getRowCount() {
return rows.size();
}
#Override
public int getColumnCount() {
return columnIdentifiers.size();
}
#Override
public String getColumnName(int column) {
Object id = null;
// This test is to cover the case when
// getColumnCount has been subclassed by mistake ...
if (column < columnIdentifiers.size() && (column >= 0)) {
id = columnIdentifiers.get(column);
}
return (id == null) ? super.getColumnName(column)
: id.toString();
}
}
public class ArrayListTableModel extends AbstractGenericTableModel<ArrayList> {
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
List<ArrayList> rows = getRowData();
return rows.get(rowIndex).get(columnIndex);
}
}
This creates two classes, an "abstract generics" based model, which allows you to specify the physical data which backs a row and a ArrayListTableModel which allows you to use a ArrayList for the individual data.
What this assumes though, is each row has the same number of elements, but it makes no checks
I suggest you have a closer look at How to Use Tables for more details
You can use the List Table Model. It will support rows of data stored in an ArrayList, Vector or any other class that implements the List interface.
The ListTableModel is also based on a generic TableModel that allows you to add Objects to a row in the model. There are many row based methods that allow easy usage of the model
The ListTableModel provides methods that allow you to easily customize the model:
setColumnClass – specify the class of individual columns so the proper renderer/editor can be used by the table.
setModelEditable – specify editable property for the entire model.
setColumnEditable – specify editable property at a column level. This property will have preference over the model editable property.
The other option is to iterate through the ArrayList and copy each row of data to a Vector and then add the Vector to the DefaultTableModel using the addRow(...) method.

Displaying sort icons in JTable header without using the build in sort mechanism

Does someone know a good way to display the sorting icons in the header of a JTable, without using the build in sort functionality?
The sorting is done by the table model (actually a database) and not by the JTable itself. Thats why the automatic display of the icons doesn't work. Maybe one can insert a dummy RowSorter that does nothing, but makes the sort icons appear?
I found a better Solution
I just wrote my own RowSorter, so that the sorting does not have any effect, but redirects the sorting request to the model instead. That way the sort order is displayed by the look and feel itself. Some Pseudocode:
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.swing.RowSorter;
import xyz.SortableTableModel;
public class MyRowSorter<M extends SortableTableModel> extends RowSorter<M> {
private M tableModel;
private List<? extends SortKey> sortKeys = new LinkedList<>();
public MyRowSorter(M tableModel) {
this.tableModel = tableModel;
}
#Override
public M getModel() {
return tableModel;
}
#Override
public void toggleSortOrder(int column) {
// redirecting sort request to model and modification of sortKeys
List<? extends SortKey> newSortKeys = ...;
setSortKeys(newSortKeys);
}
#Override
public int convertRowIndexToModel(int index) {
return index; // will always be the same
}
#Override
public int convertRowIndexToView(int index) {
return index; // will always be the same
}
#Override
public void setSortKeys(List<? extends SortKey> keys) {
if (keys == null) {
sortKeys = Collections.EMPTY_LIST;
} else {
sortKeys = Collections.unmodifiableList(keys);
}
fireSortOrderChanged();
}
#Override
public List<? extends SortKey> getSortKeys() {
return sortKeys;
}
#Override
public int getViewRowCount() {
return tableModel.getRowCount();
}
#Override
public int getModelRowCount() {
return tableModel.getRowCount();
}
// no need for any implementation
#Override public void modelStructureChanged() { }
#Override public void allRowsChanged() { }
#Override public void rowsInserted(int firstRow, int endRow) { }
#Override public void rowsDeleted(int firstRow, int endRow) { }
#Override public void rowsUpdated(int firstRow, int endRow) { }
#Override public void rowsUpdated(int firstRow, int endRow, int column) { }
}
In that case you can try to write a custom TableCellRenderer for JTableHeader.
Here is simple example of renderer:
private static class MyRenderer implements TableCellRenderer {
private ImageIcon icon1;
private ImageIcon icon2;
private TableCellRenderer defaultRenderer;
MyRenderer(JTable t){
defaultRenderer = t.getTableHeader().getDefaultRenderer();
icon1 = new ImageIcon(getClass().getResource("1.png"));
icon2 = new ImageIcon(getClass().getResource("2.png"));
}
#Override
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
Component c = defaultRenderer.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, col);
if(col%2 == 0){
((JLabel)c).setIcon(icon1);
} else {
((JLabel)c).setIcon(icon2);
}
return c;
}
}
Here icon1 and icon2 is your sorting icons.
And you can set that renderer for your JTableHeader like next:
table.getTableHeader().setDefaultRenderer(new MyRenderer(table));
table - is your JTable.
The sorting is done by the table model (actually a database) and not by the JTable itself.
Check out the DefaultRowSorter class. Maybe you use the setSortsOnUpdates(...) and setSortKeys(...) so the sorting icons match the sort from the database. You could try:
Creating an empty model
Set the sort keys
use setSortsOnUpdates(false);
Update the model using the setDataVector() (or some equivalent method if using a custom model)
Note this approach assumes you have created the TableModel with column names and no data and added the model to the JTable. I think you will then also need to use:
table.setAutoCreateColumnsFromModel(false);
to prevent the TableColumnModel from being recreated when you load the data into the model.
Solution is tricky when you want your code to work with other existing Swing layouts (I am talking about com.formdev .... flatlaf ). These L&Fs create a special Header renderer.
Here is a simple solution that will work with all main L&Fs on the market (tatoo, formdev, jgoodies). The trick is to subclass from DefaultTableCellHeaderRenderer but also to pass the table look and feel current header renderer as parameter.
// this custom renderer will display the sorting icon for all afftected columns.
class CustomTableHeaderRenderer extends DefaultTableCellHeaderRenderer implements TableCellRenderer{
final private Icon ascIcon = UIManager.getIcon("Table.ascendingSortIcon");
final private Icon descIcon = UIManager.getIcon("Table.descendingSortIcon");
TableCellRenderer iTableCellRenderer = null;
public CustomTableHeaderRenderer(TableCellRenderer tableCellRenderer)
{
iTableCellRenderer = tableCellRenderer;
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JLabel label = (JLabel) iTableCellRenderer.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column) ;
List<? extends SortKey> sortKeys = table.getRowSorter().getSortKeys();
label.setIcon(null);
for (SortKey sortKey : sortKeys) {
if (sortKey.getColumn() == table.convertColumnIndexToModel(column)){
SortOrder o = sortKey.getSortOrder();
label.setIcon(o == SortOrder.ASCENDING ? ascIcon : descIcon);
break;
}
}
return label;
}
}
yourTable.getTableHeader().setDefaultRenderer( new CustomTableHeaderRenderer( yourTable.getTableHeader().getDefaultRenderer() ));

JTable refresh are not displayed

I'm desperately trying to convince my JTable to refresh when I change its data. The data are stored in a global singleton, TreeSet, that I'm working with. Whenever the TreeSets data is altered, an event is fired to refresh the TableModel. For test purposes, I replaced this by a simple Timer firing events.
Each time an event is fired, an element from the TreeSet is removed to simulate changes in the data. The event fires and the TableClass receives it and processes it as expected, but when it comes to refreshing nothing happens. I tried to create a new TableModel each time an event occurs and set it to the global table. The changes to the singleton TreeSet are made but nothing happens to the JTable.
public class TreeTableObj implements ActionListener{
public JTable table ;
public TreeTableObj(){
MyTableModel myModel = new MyTableModel(getValuesOfTreeSet(), getTreeSetRows());
table = new JTable( myModel){ //some rendering };
table.setAutoCreateRowSorter(true);
}
class MyTableModel extends AbstractTableModel {
private String[] columnNames;
private Object[][] data ;
public MyTableModel(Object[][] data, String[] columnNames){
this.data = data;
this.columnNames = columnNames;
System.out.println("Model created");
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return data.length;
}
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
public void setData(Object[][] data){
this.data = data;
fireTableDataChanged();
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
public boolean isCellEditable(int row, int col) {
if (col < 2) {
return false;
} else {
return true;
}
}
public void setValueAt(Object value, int row, int col) {
if (true) {
System.out.println("Setting value at " + row + "," + col
+ " to " + value
+ " (an instance of "
+ value.getClass() + ")");
}
data[row][col] = value;
fireTableCellUpdated(row, col);
if (true) {
System.out.println("New value of data:");
printDebugData();
}
}
private void printDebugData() {
int numRows = getRowCount();
int numCols = getColumnCount();
for (int i=0; i < numRows; i++) {
System.out.print(" row " + i + ":");
for (int j=0; j < numCols; j++) {
System.out.print(" " + data[i][j]);
}
System.out.println();
}
System.out.println("--------------------------");
}
}
public void refreshTableModel(){
FlightsTreeSet.getInstance().remove(FlightsTreeSet.getInstance().first());
table.setModel(new MyTableModel(getValuesOfTreeSet(), getTreeSetRows()));
}
public void actionPerformed(ActionEvent arg0) {
refreshTableModel();
}
}
Any help would be appreciated!
[EDIT1]
I am getting closer to the problems Core:
I found out that the table which is displayed in the JFrame holds another reference as the one that I change. I made all changes visible and they apply to the Model.
Is something missing in your code? Your table model as it is shouldn't compile, because it is missing all the methods that JTable would use to access the data (getColumnCount(), getValueAt(), etc.)
Also, you shouldn't have to create a new model on every change - just have the model fireTableStructureChanged().
I found the problem. And leaving the answer here to be found by others...
After realizing that the displayed JTable and the one that I was editing hold different references (see [EDIT1]) I found out that I was creating two different Objects of the JTable. One to be displayed and the other one in main() to become an ActionListener. The Listener received the Events and did the changes but the other one never noticed that anything was happening.

Controlled editing of a row selection in JTable

I have a JTable displaying rows from an SQL database. The table is relatively small (only 4 columns and up to 1000 rows).
I would like to give the user the opportunity to edit any cells in the table but want to avoid restricting it so much so that they must use an edit dialog box (this makes for far easier error checking and validation but is less intuitive)
I have tried a few different ways of controlling edit selections using the valueChanged method of my JTable but haven't had much luck.
I would like each row to be edited and written to the database at the conclusion of editing. I would like that once a cell has been clicked to start the editing of that row, no other rows can be selected until the user has finished editing the row (other rows are grayed out). After editing each cell and pressing enter, the edit selection should jump to the next column in the same row.
Can anyone give pointers on how I can achieve this?
// Create table with database data
table = new JTable(new DefaultTableModel(data, columnNames)) {
public Class getColumnClass(int column) {
for (int row = 0; row < getRowCount(); row++) {
Object o = getValueAt(row, column);
if (o != null){
return o.getClass();
}
}
return Object.class;
}
#Override
public boolean isCellEditable(int row, int col){
return true;
}
#Override
public boolean editCellAt(int row, int column) {
boolean ans = super.editCellAt(row, column);
if (ans) {
Component editor = table.getEditorComponent();
editor.requestFocusInWindow();
}
return ans;
}
#Override
public void valueChanged(ListSelectionEvent source) {
super.valueChanged(source);
if (table!=null)
table.changeSelection(getSelectedRow(), getSelectedColumn()+1, false, false);
}
};
Edit - custom cell editor with table pointer seems to be a start
public class ExchangeTableCellEditor extends AbstractCellEditor implements TableCellEditor {
private JTable table;
JComponent component = new JTextField();
public ExchangeTableCellEditor(JTable table) {
this.table = table;
}
public boolean stopCellEditing() {
boolean ans = super.stopCellEditing();
//now we want to increment the cell count
table.editCellAt(table.getSelectedRow(), table.getSelectedColumn()+1);
return ans;
}
#Override
public void cancelCellEditing() {
//do nothing... must accept cell changes
}
#Override
public Object getCellEditorValue() {
return ((JTextField)component).getText();
}
#Override
public Component getTableCellEditorComponent(JTable arg0, Object value,
boolean arg2, int arg3, int arg4) {
((JTextField)component).setText((String)value);
return component;
}
}
The default renderer and editor is typically adequate for most data types, but you can define custom renderers and editors as needed.
Addendum: I'm unfamiliar with the approach shown in your fragment. Instead, register a TableModelListener with your model, as shown below, and update the database with whatever granularity is warranted. See also How to Use Tables: Listening for Data Changes.
Addendum: #kleopatra is correct about your TableCellEditor. One convenient way to notify listeners is to invoke the super implementation, as shown here. Note that the delegate invokes fireEditingStopped().
/** #see https://stackoverflow.com/questions/9155596 */
public class NewJavaGUI extends JPanel {
private final JTable table;
public NewJavaGUI() {
String[] colNames = {"C1", "C2", "C3"};
DefaultTableModel model = new DefaultTableModel(colNames, 0) {
#Override
public boolean isCellEditable(int row, int col) {
// return your actual criteria
return true;
}
#Override
public Class getColumnClass(int col) {
// return your actual type tokens
return getValueAt(0, col).getClass();
}
};
// Add data; note auto-boxing
model.addRow(new Object[]{"A1", "A2", 42});
model.addRow(new Object[]{"B1", "B2", 42d});
model.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
// DML as indicated
}
});
table = new JTable(model);
this.add(table);
}
private void display() {
JFrame f = new JFrame("NewJavaGUI");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new NewJavaGUI().display();
}
});
}
}
The behaviour you mention can be achieved by forcing your table to start editing again.
First make sure you now yourRow and Column and that you add your own tablecelleditor that extands from the AbstractCellEditor
then add this to your stopCellEditing method:
EventQueue.invokeLater(new Runnable()
{
public void run()
{
yourTable.editCellAt( yourRow, yourColumn+1);
}
});

Categories