james castillo james castillo - 2 months ago 14
Java Question

Fire Code after adding a new Row in JTable

Im looking for a way to fire a code after the jtable gets a new row. I have been reading quite alot of threads and tested them out and one of them is something like

model.fireTableDataChanged()
which I have no idea how to place.

What I would like to happen is everytime the user adds a new row, after the row has been added, the method
refreshTable()
will execute.

here is my code:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;

import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

/**
*
* @author Innocentus
*/
public class JayMultiLineTable extends JTable{

public JayMultiLineTable(){
super();
}

public JayMultiLineTable(Object[][] row, String[] col){
DefaultTableModel dtm = new DefaultTableModel(row,col);
this.setModel(dtm);
addFireEvent();
this.setDefaultRenderer(Object.class, new MultiLineCellRendererx());
this.setDefaultEditor(Object.class, new MultiLineCellEditor());
}

@Override
public void setModel(TableModel dataModel) {
if (dataModel == null) {
throw new IllegalArgumentException("Cannot set a null TableModel");
}
if (this.dataModel != dataModel) {
TableModel old = this.dataModel;
if (old != null) {
old.removeTableModelListener(this);
}
this.dataModel = dataModel;
dataModel.addTableModelListener(this);

tableChanged(new TableModelEvent(dataModel, TableModelEvent.HEADER_ROW));

firePropertyChange("model", old, dataModel);

if (getAutoCreateRowSorter()) {
setRowSorter(new TableRowSorter<TableModel>(dataModel));
}
addFireEvent();
}
try {
this.setDefaultRenderer(Object.class, new MultiLineCellRendererx());
this.setDefaultEditor(Object.class, new MultiLineCellEditor());
} catch (Exception e) {

}

}

public void refreshTable(){
JTable tbl = this;
for (int row = 0; row < tbl.getRowCount(); row++){
int rowHeight = tbl.getRowHeight();

for (int column = 0; column < tbl.getColumnCount(); column++){
Component comp = tbl.prepareRenderer(tbl.getCellRenderer(row, column), row, column);
rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
}

tbl.setRowHeight(row, rowHeight);
}
}

private void addFireEvent(){
this.getModel().addTableModelListener((TableModelEvent e) -> {
switch (e.getType()) {
case TableModelEvent.DELETE:
refreshTable();
break;
case TableModelEvent.INSERT:
refreshTable();
break;
case TableModelEvent.UPDATE:
refreshTable();
break;
}
});

}

public static void main(String args[]){
JFrame jf = new JFrame();
jf.getContentPane().setLayout(new BorderLayout());
JayMultiLineTable table = new JayMultiLineTable();
table.setModel(new DefaultTableModel(
new Object [][] {
{"this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample. this is a sample.", " this is a sample."," this is a sample."," this is a sample."}
},
new String [] {
"Title 1", "Title 2", "Title 3", "Title 4"
}
));
jf.getContentPane().add(table, BorderLayout.CENTER);
JButton btn = new JButton("add another row");
btn.addActionListener((ActionEvent e)->{
Object row[] = {"This is an added sample","This is an added sample. This is an added sample. This is an added sample. This is an added sample. This is an added sample. This is an added sample. This is an added sample. This is an added sample. This is an added sample. This is an added sample. This is an added sample. This is an added sample. This is an added sample. This is an added sample. This is an added sample","This is an added sample","This is an added sample"};
((DefaultTableModel)table.getModel()).addRow(row);
//table.refreshTable(); <--- unless I call this method, the row height will not adjust unless the addrow is fired again for the second time.
});
jf.getContentPane().add(btn, BorderLayout.NORTH);
jf.setExtendedState(JFrame.MAXIMIZED_BOTH);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.show();

}
}
class MultiLineCellRendererx extends JTextArea implements TableCellRenderer {

public MultiLineCellRendererx() {
setLineWrap(true);
setWrapStyleWord(true);
setSelectionColor(Color.GREEN);
}

@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

setText((String)value);
setSize(table.getColumnModel().getColumn(column).getWidth(),this.getPreferredSize().height);
setSelectionColor(Color.GREEN);

if (isSelected){
setBackground(table.getSelectionBackground());
setForeground(table.getSelectionForeground());
}else{
setBackground(table.getBackground());
setForeground(table.getForeground());
}
return this;
}
}

class MultiLineCellEditor extends AbstractCellEditor implements TableCellEditor {
// This is the component that will handle the editing of the cell value
JComponent component = new JTextArea();

public MultiLineCellEditor(){
((JTextArea)component).setLineWrap(true);
((JTextArea)component).setWrapStyleWord(true);
}
// This method is called when a cell value is edited by the user.
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int rowIndex, int vColIndex) {
// 'value' is value contained in the cell located at (rowIndex, vColIndex)

if (isSelected) {
// cell (and perhaps other cells) are selected
}

// Configure the component with the specified value
((JTextArea)component).setText((String)value);

// Return the configured component
return component;
}

// This method is called when editing is completed.
// It must return the new value to be stored in the cell.
public Object getCellEditorValue() {
return ((JTextArea)component).getText();
}

}


EDIT

my apologies for not explaining.

This is a customized JTable that allows multiline data without having the user to add renderers. My problem with this is when I add a row, that row will not adjust its rowheight and instead get the default size, but when I add another row, the previous row will then update and resize itself to fit the multilined text.

now what I want to achieve is to allow the jtable to fire the
refreshTable()
method after the row is inserted which will fix the issue.

now what can I do to implement this?

adding a tableModelListener that contains the
refreshTable()
fires
BEFORE
the row is inserted which is not what I am for. I want it to "refresh the table" after the row is added.

Answer

If the DefaultTableModel detects a change, it fires the corresponding event, say if you would call setRowCount with a larger number: fireTableRowsInserted(old, rowCount-1);.

Be aware that the interface TableModel is a lower requirement than DefaultTableModel, and even DefaultTableModel is derived from AbstractTableModel that has not that all firing capabilities implemented: does the firing upon changing methods. Javadoc and at least a check would be needed:

if (!(model instanceof DefaultTableModel)) {
    throw bad luck;
}

Move the addFireEvent into the setModel, so it is used for later models too. The adding of a table model listener on setting a model, might be the point to call a first refreshTable.

As refreshTable involves heavy calculation, call invokeLater to keep the GUI responsive.

In java 8

        this.getModel().addTableModelListener(e -> { // TableModelListener
            SwingUtilities.invokeLater(() -> { // Runnable
                switch (e.getType()) {
                    case TableModelEvent.DELETE:
                        refreshTable();
                        break;
                    case TableModelEvent.INSERT:
                        refreshTable();
                        break;
                    case TableModelEvent.UPDATE:
                        refreshTable();
                        break;
                }
            });
        });

Use loggers maybe to find the problem.