First, I will define a DataContainer class that holds all the data I want.
package jtbl_2tbl_abst;
/**
*
* @author Lee
*/
public class DataContainer {
private Object[][] data = {
{"Singed", "male", false},
{"Akali", "female", false},
{"Tristina", "female", true},
{"Udyr", "male", false},
{"Janna", "female", true}
};
private final String[] columnNames = {
"Name", "Sex", "Vegetarin"
};
/**
* @return the data
*/
public Object[][] getData() {
return data;
}
/**
* @param data the data to set
*/
public void setData(Object[][] data) {
this.data = data;
}
public void setData(Object data, int row, int col) {
this.data[row][col] = data;
}
/**
* @return the columnNames
*/
public String[] getColumnNames() {
return columnNames;
}
}
Now I want MyTableModel that extends AbstractTableModel.
(otherwise I should redefine MyTableModel for each table.)
package jtbl_2tbl_abst;
import javax.swing.table.AbstractTableModel;
/**
*
* @author Lee
*/
public class MyTableModel extends AbstractTableModel {
// holds column names and data from outer source
private String[] columnNames;
private Object[][] data;
public MyTableModel(Object[][] data, String[] columnNames) {
this.data = data;
this.columnNames = columnNames;
}
// the explanation is ommitted since I've covered it in the last post
// "Using AbstractTableModel"
@Override
public int getRowCount() {
return data.length;
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public Object getValueAt(int row, int col) {
return data[row][col];
}
@Override
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
@Override
public boolean isCellEditable(int row, int col) {
return true;
}
@Override
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
}
now, let's define Table1 class
package jtbl_2tbl_abst;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import net.miginfocom.swing.MigLayout;
/**
*
* @author Lee
*/
public class Table1 extends JPanel implements TableModelListener{
// JScrollPane that takes JTable
private JScrollPane scrollPane;
// This is table
private JTable table1;
// this holds data from DataContainer
DataContainer container = new DataContainer();
// this holds AbstractTableModel from MyTableModel
private AbstractTableModel model;
public Table1(DataContainer container) {
super(new MigLayout());
this.container = container;
initComponents();
}
private void initComponents() {
// initiate the table model
model = new MyTableModel(container.getData(), container.getColumnNames());
// initiate the table
table1 = new JTable(model);
// add TableModelListener so that we can change the data, and the
// changed data can be detected
table1.getModel().addTableModelListener(this);
scrollPane = new JScrollPane(table1);
this.add(scrollPane);
}
@Override
public void tableChanged(TableModelEvent e) {
// get the first row that has been changed
int row = e.getFirstRow();
// get the column that has been changed
int column = e.getColumn();
// get the changed model of the table
TableModel model = (TableModel)e.getSource();
// tableChanged method throws -1 when theres no change of row and columns
// but wants to update the whole table.
// And that causes unexpected errors, so I put if(column >=0) to prevent that
if(column >= 0 ) {
String columnName = model.getColumnName(column);
// get the changed data from the table model
Object data = model.getValueAt(row, column);
// apply the change to the container data
container.setData(data, row, column);
}
}
/**
* @return the table1
*/
public JTable getTable1() {
return table1;
}
/**
* @param table1 the table1 to set
*/
public void setTable1(JTable table1) {
this.table1 = table1;
}
/**
* @return the model
*/
public AbstractTableModel getModel() {
return model;
}
/**
* @param model the model to set
*/
public void setModel(AbstractTableModel model) {
this.model = model;
}
}
Similarly, we need to define Table2. The explanation is omitted since Table2 is identical to Table1
package jtbl_2tbl_abst;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import net.miginfocom.swing.MigLayout;
/**
*
* @author Lee
*/
public class Table2 extends JPanel implements TableModelListener{
private JScrollPane scrollPane;
private JTable table2;
private AbstractTableModel model;
DataContainer container = new DataContainer();
public Table2(DataContainer container) {
super(new MigLayout());
this.container = container;
initComponents();
}
public void initComponents() {
model = new MyTableModel(container.getData(), container.getColumnNames());
table2 = new JTable(model);
table2.getModel().addTableModelListener(this);
scrollPane = new JScrollPane(table2);
this.add(scrollPane);
}
@Override
public void tableChanged(TableModelEvent e) {
int row = e.getFirstRow();
int column = e.getColumn();
TableModel model = (TableModel)e.getSource();
if(column >= 0 ) {
String columnName = model.getColumnName(column);
Object data = model.getValueAt(row, column);
System.out.println("changed data: " + data);
container.setData(data, row, column);
}
}
/**
* @return the table2
*/
public JTable getTable2() {
return table2;
}
/**
* @param table2 the table2 to set
*/
public void setTable2(JTable table2) {
this.table2 = table2;
}
/**
* @return the model
*/
public AbstractTableModel getModel() {
return model;
}
/**
* @param model the model to set
*/
public void setModel(AbstractTableModel model) {
this.model = model;
}
}
Now we need JFrame that holds the two tables.
package jtbl_2tbl_abst;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
/**
*
* @author Lee
*/
public class Frame {
// JFrame that holds Table1
JFrame frame1 = new JFrame();
// JFrame that holds Table2
JFrame frame2 = new JFrame();
// This button will open frame2 with Table2
JButton opnBttn = new JButton("Open Table2");
// This button will close frame2 updating the changed data
JButton mdfBttn = new JButton("OK");
// This button will Update frame2(table2) when table1 has been changed
JButton udtBttn = new JButton("Update");
DataContainer container = new DataContainer();
Table1 table1Panel;
Table2 table2Panel;
public Frame() {
initTable1();
initTable2();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Frame();
}
});
}
// initiate Table1
public void initTable1() {
// add ActionListener to opnBttn
opnBttn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
opnBttnClicked(evt);
}
});
// add ActionListener to udtBttn
udtBttn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
udtBttnClicked(evt);
}
});
// initiate Table1 with the sharing data
table1Panel = new Table1(container);
// setup frame1
frame1.setLayout(new MigLayout());
frame1.add(table1Panel, "span, growx, wrap");
frame1.add(opnBttn);
frame1.add(udtBttn);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.pack();
frame1.setVisible(true);
}
// initiate Table2
public void initTable2() {
// add ActionListener to mdfBttn
mdfBttn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
mdfBttnClicked(evt);
}
});
// initiate Table2 with the sharing data
table2Panel = new Table2(container);
// set up frame2
frame2.setLayout(new MigLayout());
frame2.add(table2Panel, "wrap");
frame2.add(mdfBttn, "center");
frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame2.pack();
}
// when opnBttn is clicked, frame2(table2) become visible
public void opnBttnClicked(ActionEvent evt) {
// clikcing the opnBttn when one is editing the table cell will cause
// data lose since the changed value is still in Editor, not in Renderer.
// To send the changed value from Editor to Render, I need the following
if (table1Panel.getTable1().isEditing()) {
table1Panel.getTable1().getCellEditor().stopCellEditing();
}
// open frame2(table2)
frame2.setVisible(true);
}
// when mdfBttn is clicked, frame2 is closed
public void mdfBttnClicked(ActionEvent evt) {
// same as above
if (table2Panel.getTable2().isEditing()) {
table2Panel.getTable2().getCellEditor().stopCellEditing();
}
frame2.setVisible(false);
// notify table1 that the data may have been changed
table1Panel.getModel().fireTableDataChanged();
}
// when udtBttn is clicked, table2 refreshes
public void udtBttnClicked(ActionEvent evt) {
if (table1Panel.getTable1().isEditing()) {
table1Panel.getTable1().getCellEditor().stopCellEditing();
}
table2Panel.getModel().fireTableDataChanged();
}
}
No comments:
Post a Comment