大家好日子,
我遇到了正在处理的程序问题。简要说明一下,我有一个包含多个列和行的JTable。特定列具有可编辑字段,当改变值时,其他列值根据输入的数据而改变。一切正常但是当我向JTable添加过滤器选项时,对可编辑列所做的更改将不会在应用过滤器后按预期更改其他列的值。我附上了几张图片来说明问题。
第一张图片显示未经过滤的表格正常工作。更改折扣列值将使输入折扣的百分比减少存储在GPL列中的相应价格,并显示在SP列的相应行中。更改数量列值将相应的SP列价格与输入的数量相乘,并显示在总计列的相应行中。
Unfiltered table http://i36.tinypic.com/2pq7r50.png
第二张图片显示过滤后的表格未按预期工作。更改“折扣”或“数量”列中的值不会更改预期的列。
Filtered table http://i33.tinypic.com/25hcr35.png
我添加了下面包含2个类的SSCCE代码。第一个是表本身,第二个是表的监听器。
编辑我根据camickr的回答更改了该类的代码,现在完全有效。
TableCellChange类
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.math.BigDecimal;
import java.math.MathContext;
import java.text.DecimalFormat;
import java.util.Locale;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public final class TableCellChange extends JPanel {
private static JFrame frameTableCellChange;
private JPanel panelTable, panelButtons;
private JButton buttonResetDiscounts, buttonResetQuantities, buttonExit;
private JTextField textFilterBox, quantityField, discountField;
private JLabel labelFilter;
private DefaultTableModel tableModel;
private JTable table;
private TableRowSorter<DefaultTableModel> sorter;
private TableColumn columnDiscount, columnTotal, columnQuantity;
private TableCellListener tableCellListener;
private String checkForNull;
private DecimalFormat decimalFormatUS;
private Locale localeUSFormat;
private BigDecimal valueDiscount, valueGPL, resultDiscount, resultSP, resultTotal,
backupDiscount = new BigDecimal("0");
private int selectedColumnIndex, selectedRowIndex, valueQuantity, backupQuantity = 1;
public TableCellChange() {
super();
panelTable = new JPanel();
panelButtons = new JPanel();
setLayout(new BorderLayout());
createTable();
createButtons();
add(panelTable, BorderLayout.NORTH);
add(panelButtons, BorderLayout.CENTER);
// Always focus on the JTextField when opening the window
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
textFilterBox.requestFocusInWindow();
}
});
} // -> TableCellChange()
// Create the buttons for the query result window
public void createButtons() {
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints gridcons = new GridBagConstraints();
gridcons.fill = GridBagConstraints.HORIZONTAL;
panelButtons.setLayout(gridbag);
labelFilter = new JLabel("Quick search:");
gridcons.insets = new Insets(5,0,0,0);
gridcons.gridx = 0;
gridcons.gridy = 0;
gridcons.gridwidth = 2;
gridbag.setConstraints(labelFilter, gridcons);
labelFilter.setHorizontalAlignment(JLabel.CENTER);
panelButtons.add(labelFilter);
// Create text field for filtering
textFilterBox = new JTextField();
gridcons.insets = new Insets(5,0,0,0);
gridcons.gridx = 0;
gridcons.gridy = 1;
gridcons.gridwidth = 2;
gridbag.setConstraints(textFilterBox, gridcons);
textFilterBox.getDocument().addDocumentListener(
new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
tableFilter();
}
@Override
public void insertUpdate(DocumentEvent e) {
tableFilter();
}
@Override
public void removeUpdate(DocumentEvent e) {
tableFilter();
}
}); // -> DocumentListener()
panelButtons.add(textFilterBox);
// Create the button to reset the discount column to 0%
buttonResetDiscounts = new JButton("Reset all discounts");
buttonResetDiscounts.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
BigDecimal valueGPL, valueTotal;
int valueQuantity;
for (int i = 0; i < table.getModel().getRowCount(); i++) {
valueGPL = new BigDecimal( table.getModel().
getValueAt(i, 2).toString().replaceAll("[$,]", "") );
table.getModel().setValueAt("0%", i, 3);
table.getModel().setValueAt(DecimalFormat
.getCurrencyInstance(localeUSFormat).format(valueGPL), i, 4);
valueQuantity = Integer.parseInt( table.getModel().
getValueAt(i, 5).toString() );
valueTotal = valueGPL.multiply(new BigDecimal(valueQuantity),
new MathContext(BigDecimal.ROUND_HALF_EVEN));
table.getModel().setValueAt(DecimalFormat
.getCurrencyInstance(localeUSFormat).format(valueTotal), i, 6);
}
}
});
gridcons.insets = new Insets(10,0,0,0);
gridcons.gridx = 0;
gridcons.gridy = 3;
gridcons.gridwidth = 1;
gridbag.setConstraints(buttonResetDiscounts, gridcons);
panelButtons.add(buttonResetDiscounts);
// Create button to reset the quantity column to 1
buttonResetQuantities = new JButton("Reset all quantities");
buttonResetQuantities.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
BigDecimal valueSP;
for (int i = 0; i < table.getModel().getRowCount(); i++) {
valueSP = new BigDecimal( table.getModel().
getValueAt(i, 4).toString().replaceAll("[$,]", "") );
table.getModel().setValueAt("1", i, 5);
table.getModel().setValueAt(DecimalFormat.
getCurrencyInstance(localeUSFormat).format(valueSP), i, 6);
}
}
});
gridcons.insets = new Insets(10,0,0,0);
gridcons.gridx = 1;
gridcons.gridy = 3;
gridcons.gridwidth = 1;
gridbag.setConstraints(buttonResetQuantities, gridcons);
panelButtons.add(buttonResetQuantities);
// Create button for closing the window and releasing resources
buttonExit = new JButton("Exit");
buttonExit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
});
gridcons.insets = new Insets(5,0,0,0);
gridcons.gridx = 0;
gridcons.gridy = 5;
gridcons.gridwidth = 2;
gridbag.setConstraints(buttonExit, gridcons);
panelButtons.add(buttonExit);
} // -> createButtons()
// Filters the JTable based on user input
private void tableFilter() {
RowFilter<DefaultTableModel, Object> tableRowFilter;// = null;
// If current expression doesn't parse, don't update
try {
tableRowFilter = RowFilter.regexFilter("(?i)" + textFilterBox.
getText(), 0, 1, 2);
} catch (java.util.regex.PatternSyntaxException e) {
return;
}
sorter.setRowFilter(tableRowFilter);
} // -> tableFilter
// Method that creates the JTable
public void createTable() {
// Create listener for selecting all text when a text field gains focus
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("permanentFocusOwner", new PropertyChangeListener() {
@Override
public void propertyChange(final PropertyChangeEvent e) {
if (e.getNewValue() instanceof JTextField) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JTextField textField = (JTextField)e.getNewValue();
textField.selectAll();
}
});
}
}
});
String[] columnNames = {"Model", "Description", "GPL", "Discount", "SP",
"Quantity", "Total"};
Object[][] data = {
{"MR16", "desc1", "$649.00", "0%", "$649.00", new Integer(1), "$649.00"},
{"MR24", "desc2", "$1,199.00", "0%", "$1,199.00", new Integer(1), "1,199.00"},
{"MR62", "desc3", "$699.00", "0%", "$699.00", new Integer(1), "$699.00"},
{"MR66", "desc4", "$1,299.00", "0%", "$1,299.00", new Integer(1), "$1,299.00"},
{"MX80", "desc5", "$1,995.00", "0%", "$1,995.00", new Integer(1), "$1,995.00"},
{"MX90", "desc6", "$3,995.00", "0%", "$3,995.00", new Integer(1), "$3,995.00"},
{"MX400", "desc7", "$15,995.00", "0%", "$15,995.00", new Integer(1), "$15,995.00"},
{"MX600", "desc8", "$31,995.00", "0%", "$31,995.00", new Integer(1), "$31,995.00"},
{"MS22-HW", "desc9", "$1,999.00", "0%", "$1,999.00", new Integer(1), "$1,999.00"},
{"MS42-HW", "desc10", "$3,499.00", "0%", "$3,499.00", new Integer(1), "$3,499.00"},
};
// Create the TableModel and populate it
tableModel = new DefaultTableModel(data, columnNames) {
Class [] classes = {String.class, String.class, String.class,
String.class, String.class, int.class, String.class, Boolean.class};
@Override
public Class getColumnClass(int column) {
return classes[column];
}
};
// Create a JTable and populate it with the content of the TableModel
table = new JTable(tableModel) {
@Override
public boolean isCellEditable(int row, int column) {
if (column == 0 || column == 1 || column == 2 || column == 4 ||
column == 6) {
return false;
}
return true;
}
};
// This sorter is used for text filtering
sorter = new TableRowSorter<>(tableModel);
for (int column = 3; column < 6; column++) {
sorter.setSortable(column, false);
}
table.setRowSorter(sorter);
columnTotal= table.getColumnModel().getColumn(6);
columnTotal.setPreferredWidth(100);
// Filter user input in the quantity text field to only allow digits
discountField =new JTextField();
discountField.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e)
{
if(!Character.isDigit(e.getKeyChar()) && e.getKeyChar() !=KeyEvent.VK_BACK_SPACE) {
discountField.setEditable(false);
discountField.setBackground(Color.WHITE);
} else {
discountField.setEditable(true);
}
}
});
// Set the text field to the cells of the quantity column
columnQuantity = table.getColumnModel().getColumn(5);
columnQuantity.setCellEditor(new DefaultCellEditor (discountField));
// Filter user input in the discount text field to only allow digits
quantityField =new JTextField();
quantityField.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e)
{
if(!Character.isDigit(e.getKeyChar()) && e.getKeyChar() !=KeyEvent.VK_BACK_SPACE) {
quantityField.setEditable(false);
quantityField.setBackground(Color.WHITE);
//JOptionPane.showMessageDialog(null,"Only digit input is allowed!");
} else {
quantityField.setEditable(true);
}
}
});
// Set the text field to the cells of the quantity column
columnDiscount = table.getColumnModel().getColumn(3);
columnDiscount.setCellEditor(new DefaultCellEditor(discountField));
// Create an US number format
localeUSFormat = Locale.US;
decimalFormatUS = (DecimalFormat) DecimalFormat.getInstance(localeUSFormat);
decimalFormatUS.setMaximumFractionDigits(2);
// Create abstract action which listens for changes made in the JTable
Action actionTableListener = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
TableCellListener tcl = (TableCellListener)e.getSource();
// Get the current row and column index of the table
selectedRowIndex = tcl.getRow();
selectedColumnIndex = tcl.getColumn();
TableModel model = tcl.getTable().getModel();
// Have a string variable check for null cell value
checkForNull = model.getValueAt(selectedRowIndex,selectedColumnIndex).toString();
// Change the discounted and total price values
if (selectedColumnIndex == 3) {
// Check if the discount value is null and replace with
// last used value if true
if (checkForNull.equals("")) {
model.setValueAt(backupDiscount + "%",selectedRowIndex, selectedColumnIndex);
return;
}
// Get the discount value and replace the '%' with nothing
valueDiscount = new BigDecimal(( model
.getValueAt(selectedRowIndex,selectedColumnIndex)
.toString().replaceAll("[%]","") ));
//
model.setValueAt(valueDiscount + "%",selectedRowIndex, selectedColumnIndex);
// Check if the discount value is greater than 100
if ( (valueDiscount.compareTo(new BigDecimal(100)) == 1 ) ) {
model.setValueAt(backupDiscount + "%",selectedRowIndex, selectedColumnIndex);
JOptionPane.showMessageDialog(null,"Discount cannot be more than 100%.");
} else {
backupDiscount = valueDiscount;
valueDiscount = valueDiscount.divide(new BigDecimal(100)
, 2, BigDecimal.ROUND_HALF_EVEN);
// Calculate SP and Total values based on the discount input
valueGPL = new BigDecimal( ( model
.getValueAt(selectedRowIndex,selectedColumnIndex - 1)
.toString().replaceAll("[$,]","") ) );
// Get the quantity value
valueQuantity = Integer.parseInt( ( model
.getValueAt(selectedRowIndex,selectedColumnIndex + 2)
.toString() ) );
// Calculate the new discount value
resultDiscount = valueGPL.multiply(valueDiscount,
new MathContext(BigDecimal.ROUND_HALF_EVEN));
// Calculate the new SP value
resultSP = valueGPL.subtract(resultDiscount,
new MathContext(BigDecimal.ROUND_HALF_EVEN));
// Calculate the new result value
resultTotal = resultSP.multiply(new BigDecimal(valueQuantity),
new MathContext(BigDecimal.ROUND_HALF_EVEN));
// Display the new SP value
model.setValueAt(DecimalFormat.getCurrencyInstance(localeUSFormat)
.format(resultSP),selectedRowIndex, selectedColumnIndex + 1);
// Display the new Total value
model.setValueAt(DecimalFormat.getCurrencyInstance(localeUSFormat)
.format(resultTotal),selectedRowIndex, selectedColumnIndex + 3);
}
}
// Change the total price values based on the quantity column
if (selectedColumnIndex == 5) {
// Check if the quantity value is null and replace with
// last used value if true
if (checkForNull.equals("")) {
model.setValueAt(backupQuantity,selectedRowIndex, selectedColumnIndex);
return;
}
// Change total price value based on the quantity column
resultSP = new BigDecimal( ( model.
getValueAt(selectedRowIndex,
selectedColumnIndex - 1).toString().replaceAll("[$,]","") ) );
valueQuantity = Integer.parseInt( ( model.getValueAt(selectedRowIndex,
selectedColumnIndex).toString() ) );
// Check if the value quantity is over a certain limit
if (valueQuantity <= 0 || valueQuantity >= 999999) {
model.setValueAt(backupQuantity,selectedRowIndex, selectedColumnIndex);
JOptionPane.showMessageDialog(null,"Quantity value is too high or invalid!");
} else {
// If the value is under the limit: backup the new quantity
// value, calculate the new total value and display it
backupQuantity = valueQuantity;
resultTotal = resultSP.multiply(new BigDecimal(valueQuantity),
new MathContext(BigDecimal.ROUND_HALF_EVEN));
model.setValueAt(DecimalFormat.getCurrencyInstance(localeUSFormat)
.format(resultTotal), selectedRowIndex, selectedColumnIndex + 1);
}
}
}
}; // -> AbstractAction()
tableCellListener = new TableCellListener(table, actionTableListener);
table.setPreferredScrollableViewportSize(table.
getPreferredSize());
table.setRowHeight(22);
setVisibleRowCount(table,10);
table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
table.setFillsViewportHeight(true);
table.getTableHeader().setReorderingAllowed(false);
table.getTableHeader().setResizingAllowed(false);
panelTable.add(new JScrollPane(table));
} // -> createTable()
// Method to display a fixed number of rows in the JTable viewport
public static void setVisibleRowCount(JTable table, int rows){
int height = 0;
for(int row=0; row<rows; row++) {
height += table.getRowHeight(row);
}
table.setPreferredScrollableViewportSize(new Dimension(
table.getPreferredScrollableViewportSize().width, height ));
}
// Create and display the contents of the frame
public static void showGUI() {
// Disable boldface controls
UIManager.put("swing.boldMetal", Boolean.FALSE);
// Create the frame
frameTableCellChange = new JFrame("Table frame");
frameTableCellChange.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frameTableCellChange.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent we) {
System.exit(0);
}
});
// Create and set up the content pane.
TableCellChange newContentPane = new TableCellChange();
newContentPane.setOpaque(true); //content panes must be opaque
frameTableCellChange.setContentPane(newContentPane);
// Arrange and display the window.
frameTableCellChange.pack(); //must be called first
frameTableCellChange.setLocationRelativeTo(null); //center window
frameTableCellChange.setResizable(false);
frameTableCellChange.setVisible(true);
} //-> showQueryResultGUI()
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.
UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException | InstantiationException |
IllegalAccessException |
javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(TableCellChange.class.getName()).
log(java.util.logging.Level.SEVERE, null, ex);
}
// Display the frame and it's contents
TableCellChange.showGUI();
}
});
} //-> main(String[] args)
} //-> TableCellChange class
编辑这个班级是由Rob Camick(a.k.a。camickr)创建的,所有学分都归他所有,因为他创造了这个令人敬畏的代码。只有注释从代码中删除才能尊重字符限制。
TableCellListener类
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Action;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
/*
* This class listens for changes made to the data in the table via the
* TableCellEditor. When editing is started, the value of the cell is saved
* When editing is stopped the new value is saved. When the old and new
* values are different, then the provided Action is invoked.
* The source of the Action is a TableCellListener instance.
*/
public class TableCellListener implements PropertyChangeListener, Runnable {
private JTable table;
private Action action;
private int row;
private int column;
private Object oldValue;
private Object newValue;
public TableCellListener(JTable table, Action action) {
this.table = table;
this.action = action;
this.table.addPropertyChangeListener(this);
}
private TableCellListener(JTable table, int row, int column, Object oldValue, Object newValue) {
this.table = table;
this.row = row;
this.column = column;
this.oldValue = oldValue;
this.newValue = newValue;
}
public int getColumn() {
return column;
}
public Object getNewValue() {
return newValue;
}
public Object getOldValue() {
return oldValue;
}
public int getRow() {
return row;
}
public JTable getTable() {
return table;
}
@Override
public void propertyChange(PropertyChangeEvent e) {
if ("tableCellEditor".equals(e.getPropertyName())) {
if (table.isEditing()) {
processEditingStarted();
} else {
processEditingStopped();
}
}
}
private void processEditingStarted() {
SwingUtilities.invokeLater(this);
}
@Override
public void run() {
row = table.convertRowIndexToView(table.getEditingRow());
row = table.getEditingRow();
column = table.convertColumnIndexToModel(table.getEditingColumn());
oldValue = table.getModel().getValueAt(row, column);
newValue = null;
}
private void processEditingStopped() {
newValue = table.getModel().getValueAt(row, column);
if (!newValue.equals(oldValue)) {
TableCellListener tcl = new TableCellListener(
getTable(), getRow(), getColumn(), getOldValue(), getNewValue());
ActionEvent event = new ActionEvent(
tcl,
ActionEvent.ACTION_PERFORMED,
"");
action.actionPerformed(event);
}
}
}
据我所知,在过滤表时,表视图的索引会发生变化,并且必须与基础模型的索引同步。如何才能使过滤后的表工作?
感谢您阅读并度过美好的一天。 :)
答案 0 :(得分:4)
您可能需要执行从视图到模型的转换。请查看如何使用表教程的Sorting and Filtering部分:
当表使用分拣机时,用户看到的数据可能位于a 不同于数据模型指定的顺序,也可能不是 包括数据模型指定的所有行。用户的数据 实际上看到被称为视图,并有自己的一套 坐标。 JTable提供了从模型转换的方法 坐标来查看坐标 - convertColumnIndexToView和 convertRowIndexToView - 从视图坐标转换为 模型坐标 - convertColumnIndexToModel和 convertRowIndexToModel。
编辑:从视图(表格)转换为模型:
替换这些行:
row = table.convertRowIndexToView(table.getEditingRow());
row = table.getEditingRow();
使用:
row = table.convertRowIndexToModel(table.getEditingRow());
答案 1 :(得分:3)
您错误地为TableCellListener实现了Action。您无法使用所选的行/列,因为这些值位于“表”视图中。 TableCellListener适用于模型。
查看Table Cell Editor提供的示例操作。要获取已更改的行/列,必须引用TableCellListener本身。
编辑:
这是我的简单文字示例。当您更改“价格”时,“价格变动”和“价值”列会自动更新。
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableCellListenerTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI()
{
String[] columnNames = {"Stock", "Shares", "Price", "Price Change", "Value"};
Object[][] data =
{
{"IBM", new Integer(100), new Double(85), new Double(0), new Double(8500)},
{"Apple", new Integer(300), new Double(30), new Double(0), new Double(9000)},
{"Sun", new Integer(1500), new Double(5), new Double(0), new Double(7500)},
{"Google", new Integer(100), new Double(100), new Double(0), new Double(10000)}
};
DefaultTableModel model = new DefaultTableModel(data, columnNames)
{
public Class getColumnClass(int column)
{
return getValueAt(0, column).getClass();
}
public boolean isCellEditable(int row, int column)
{
return column == 2;
}
};
JTable table = new JTable(model);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
// Add a sorter
TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<DefaultTableModel>(model);
table.setRowSorter(sorter);
// Filter
try
{
RowFilter<DefaultTableModel, Object> rf = RowFilter.regexFilter("l", 0);
sorter.setRowFilter(rf);
}
catch (java.util.regex.PatternSyntaxException e) {}
Action action = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
TableCellListener tcl = (TableCellListener)e.getSource();
int column = tcl.getColumn();
if (column == 2)
{
int row = tcl.getRow();
double oldPrice = ((Double)tcl.getOldValue()).doubleValue();
double newPrice = ((Double)tcl.getNewValue()).doubleValue();
TableModel model = tcl.getTable().getModel();
double priceChange = new Double(newPrice - oldPrice);
model.setValueAt(priceChange, row, 3);
double shares = ((Integer)model.getValueAt(row, 1)).doubleValue();
Double value = new Double(shares * newPrice);
model.setValueAt(value, row, 4);
}
}
};
TableCellListener tcl = new TableCellListener(table, action);
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("Table Cell Listener");
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.add( scrollPane );
frame.setSize(400, 160);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}