交互式 JTable
An Interactive JTable
我正在开发一个使用 JTable 的简单 Java 应用程序。我正在尝试实现我所看到的 here。 link 上的 table 的行为与我在 table 中想要的行为完全相同。我所做的与引用 link 上显示的完全相同。 但是,当我 运行 我的应用程序时,我无法编辑第三列和最后一列。
我想要的行为是,当用户在填写 table 中的最后一列后按 Enter 时,应用程序应添加一个空行。
这是我的第一个 class,实体 class:
public class Product {
String productDescription;
String productCode;
double productPrice;
//dummy properties
int productQuantity;
double productTotalAmount;
public Product() {
this.productDescription = "";
this.productCode = "";
this.productPrice = 0;
this.productTotalAmount = 0;
this.productQuantity = 0;
}
public int getProductQuantity() {
return productQuantity;
}
public void setProductQuantity(int productQuantity) {
this.productQuantity = productQuantity;
}
public double getProductTotalAmount() {
return productTotalAmount;
}
public void setProductTotalAmount(double productTotalAmount) {
this.productTotalAmount = productTotalAmount;
}
public String getProductDescription() {
return productDescription;
}
public void setProductDescription(String productDescription) {
this.productDescription = productDescription;
}
public String getProductCode() {
return productCode;
}
public void setProductCode(String productCode) {
this.productCode = productCode;
}
public double getProductPrice() {
return productPrice;
}
public void setProductPrice(double productPrice) {
this.productPrice = productPrice;
}}
这是我的第二个 class,table 型号:
public class InteractiveTableModel extends AbstractTableModel{
// indexes
public static final int PRODUCT_DESCRIPTION_INDEX = 0;
public static final int PRODUCT_CODE_INDEX = 1;
public static final int PRODUCT_PRICE_INDEX = 2;
public static final int PRODUCT_QUANTITY_INDEX=3;
public static final int PRODUCT_TOTAL_AMOUNT_INDEX=4;
public static final int HIDDEN_INDEX = 5;
protected String [] columnNames;
protected Vector dataVector;
//this constructor is responsible for initializing the columnmanes and the data of the table.
public InteractiveTableModel(String[] columnNames) {
this.columnNames = columnNames;
dataVector = new Vector();
}
@Override
//this method returns the name of the column corresponding to the passed integer.
public String getColumnName(int column) {
return columnNames[column];
}
//this method is used to determine whether a cell is editable
//this is for manuvouring to the next editable cell
@Override
public boolean isCellEditable(int row, int column) {
if (column == HIDDEN_INDEX) return false;
else return true;
}
@Override
public Class getColumnClass(int column) {// returns a class representing the datatype of the data stored in that column
switch (column) {
case PRODUCT_DESCRIPTION_INDEX:
case PRODUCT_CODE_INDEX:
return String.class;
case PRODUCT_QUANTITY_INDEX:
return Integer.class;
case PRODUCT_PRICE_INDEX:
case PRODUCT_TOTAL_AMOUNT_INDEX:
return double.class;
default:
return Object.class;
}
}
@Override
public int getRowCount() {
return dataVector.size();
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public Object getValueAt(int row, int column) {
Product record = (Product)dataVector.get(row);//this will return a row in a vector
//this is cast to AudioRecord so that we can extract the data that we want correspoinding to column number
switch (column) {
case PRODUCT_DESCRIPTION_INDEX:
return record.getProductDescription();
case PRODUCT_CODE_INDEX:
return record.getProductCode();
case PRODUCT_PRICE_INDEX:
return record.getProductPrice();
case PRODUCT_TOTAL_AMOUNT_INDEX:
return record.getProductTotalAmount();
case PRODUCT_QUANTITY_INDEX:
return record.getProductQuantity();
default:
return new Object();
}
}
//setting data in a cell
@Override
public void setValueAt(Object value, int row, int column) {
Product record = (Product)dataVector.get(row);
switch (column) {
case PRODUCT_DESCRIPTION_INDEX:
record.setProductDescription((String)value);
break;
case PRODUCT_CODE_INDEX:
record.setProductCode((String)value);
break;
case PRODUCT_PRICE_INDEX:
record.setProductPrice((Double)value);
break;
case PRODUCT_QUANTITY_INDEX:
record.setProductQuantity((Integer)value);
break;
case PRODUCT_TOTAL_AMOUNT_INDEX:
record.setProductTotalAmount((Double)value);
break;
default:
System.out.println("invalid index");
}
fireTableCellUpdated(row, column);//this is for updating the cell
}
public boolean hasEmptyRow() {
if (dataVector.isEmpty()) return false;
Product productRecord = (Product)dataVector.get(dataVector.size() - 1);
if (productRecord.getProductDescription().trim().equals("") &&
productRecord.getProductCode().trim().equals("") &&
(productRecord.getProductPrice()!=1 &&
productRecord.getProductQuantity()!=1 &&
productRecord.getProductTotalAmount()!=1))
{
return true;
}
else return false;
}
public void addEmptyRow() {
dataVector.add(new Product());
fireTableRowsInserted(
dataVector.size() - 1,
dataVector.size() - 1);
}}
最后的class如下:
public final class InteractiveForm extends JPanel {
public static final String[] columnNames = {
"Quantity", "Product Code", "Product Description", "Product Price","Product Quantity",""
};
protected JTable table;
protected JScrollPane scroller;
protected InteractiveTableModel tableModel;
public InteractiveForm() {
initComponent();
}
public void initComponent() {
tableModel = new InteractiveTableModel(columnNames);
tableModel.addTableModelListener(new InteractiveForm.InteractiveTableModelListener());
table = new JTable();
table.setModel(tableModel);
table.setSurrendersFocusOnKeystroke(true);
if (!tableModel.hasEmptyRow()) {
tableModel.addEmptyRow();
}
scroller = new javax.swing.JScrollPane(table);
table.setPreferredScrollableViewportSize(new java.awt.Dimension(500, 300));
TableColumn hidden = table.getColumnModel().getColumn(InteractiveTableModel.HIDDEN_INDEX);
hidden.setMinWidth(2);
hidden.setPreferredWidth(2);
hidden.setMaxWidth(2);
hidden.setCellRenderer(new InteractiveRenderer(InteractiveTableModel.HIDDEN_INDEX));
setLayout(new BorderLayout());
add(scroller, BorderLayout.CENTER);
}
public void highlightLastRow(int row) {
int lastrow = tableModel.getRowCount();//return the number of rows - last row
if (row == lastrow - 1) {
table.setRowSelectionInterval(lastrow - 1, lastrow - 1);
} else {
table.setRowSelectionInterval(row + 1, row + 1);
}
table.setColumnSelectionInterval(0, 0);//setting it to the first column
}
class InteractiveRenderer extends DefaultTableCellRenderer {
protected int interactiveColumn;
public InteractiveRenderer(int interactiveColumn) {
this.interactiveColumn = interactiveColumn;
}
@Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column)
{
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (column == interactiveColumn && hasFocus) {
if ((InteractiveForm.this.tableModel.getRowCount() - 1) == row &&
!InteractiveForm.this.tableModel.hasEmptyRow())
{
InteractiveForm.this.tableModel.addEmptyRow();//this is where we append a new row
}
highlightLastRow(row);//making it get focus and highlighted
}
return c;//returning the component
}
}
public class InteractiveTableModelListener implements TableModelListener {
@Override
public void tableChanged(TableModelEvent evt) {
if (evt.getType() == TableModelEvent.UPDATE) {
int column = evt.getColumn();
int row = evt.getFirstRow();
System.out.println("row: " + row + " column: " + column);
table.setColumnSelectionInterval(column + 1, column + 1);
table.setRowSelectionInterval(row, row);
}
}
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
JFrame frame = new JFrame("Interactive Form");
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
frame.getContentPane().add(new InteractiveForm());
frame.pack();
frame.setVisible(true);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException | HeadlessException e) {
}
}}
我与上面 link 上的 table 的唯一区别是添加了我自己的变量,这些变量不应该 work.The 逻辑在逃避我这里。
如何让我的 table 的行为方式与 this one 相同?
您正在设置空值。它适用于 Double.class 而不是 double.class。
public class Product {
String productDescription;
String productCode;
Double productPrice;
//dummy properties
Double productQuantity;
Double productTotalAmount;
..
@Override
public Class getColumnClass(int column) {// returns a class representing the datatype of the data stored in that column
switch (column) {
case PRODUCT_DESCRIPTION_INDEX:
case PRODUCT_CODE_INDEX:
return String.class;
case PRODUCT_QUANTITY_INDEX:
case PRODUCT_PRICE_INDEX:
case PRODUCT_TOTAL_AMOUNT_INDEX:
return Double.class;
default:
return Object.class;
}
}
...
更新
与 boxing/unboxing 有关的东西。我知道该由谁来修复它,但我仍然不知道真正的原因。
我正在开发一个使用 JTable 的简单 Java 应用程序。我正在尝试实现我所看到的 here。 link 上的 table 的行为与我在 table 中想要的行为完全相同。我所做的与引用 link 上显示的完全相同。 但是,当我 运行 我的应用程序时,我无法编辑第三列和最后一列。 我想要的行为是,当用户在填写 table 中的最后一列后按 Enter 时,应用程序应添加一个空行。
这是我的第一个 class,实体 class:
public class Product {
String productDescription;
String productCode;
double productPrice;
//dummy properties
int productQuantity;
double productTotalAmount;
public Product() {
this.productDescription = "";
this.productCode = "";
this.productPrice = 0;
this.productTotalAmount = 0;
this.productQuantity = 0;
}
public int getProductQuantity() {
return productQuantity;
}
public void setProductQuantity(int productQuantity) {
this.productQuantity = productQuantity;
}
public double getProductTotalAmount() {
return productTotalAmount;
}
public void setProductTotalAmount(double productTotalAmount) {
this.productTotalAmount = productTotalAmount;
}
public String getProductDescription() {
return productDescription;
}
public void setProductDescription(String productDescription) {
this.productDescription = productDescription;
}
public String getProductCode() {
return productCode;
}
public void setProductCode(String productCode) {
this.productCode = productCode;
}
public double getProductPrice() {
return productPrice;
}
public void setProductPrice(double productPrice) {
this.productPrice = productPrice;
}}
这是我的第二个 class,table 型号:
public class InteractiveTableModel extends AbstractTableModel{
// indexes
public static final int PRODUCT_DESCRIPTION_INDEX = 0;
public static final int PRODUCT_CODE_INDEX = 1;
public static final int PRODUCT_PRICE_INDEX = 2;
public static final int PRODUCT_QUANTITY_INDEX=3;
public static final int PRODUCT_TOTAL_AMOUNT_INDEX=4;
public static final int HIDDEN_INDEX = 5;
protected String [] columnNames;
protected Vector dataVector;
//this constructor is responsible for initializing the columnmanes and the data of the table.
public InteractiveTableModel(String[] columnNames) {
this.columnNames = columnNames;
dataVector = new Vector();
}
@Override
//this method returns the name of the column corresponding to the passed integer.
public String getColumnName(int column) {
return columnNames[column];
}
//this method is used to determine whether a cell is editable
//this is for manuvouring to the next editable cell
@Override
public boolean isCellEditable(int row, int column) {
if (column == HIDDEN_INDEX) return false;
else return true;
}
@Override
public Class getColumnClass(int column) {// returns a class representing the datatype of the data stored in that column
switch (column) {
case PRODUCT_DESCRIPTION_INDEX:
case PRODUCT_CODE_INDEX:
return String.class;
case PRODUCT_QUANTITY_INDEX:
return Integer.class;
case PRODUCT_PRICE_INDEX:
case PRODUCT_TOTAL_AMOUNT_INDEX:
return double.class;
default:
return Object.class;
}
}
@Override
public int getRowCount() {
return dataVector.size();
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public Object getValueAt(int row, int column) {
Product record = (Product)dataVector.get(row);//this will return a row in a vector
//this is cast to AudioRecord so that we can extract the data that we want correspoinding to column number
switch (column) {
case PRODUCT_DESCRIPTION_INDEX:
return record.getProductDescription();
case PRODUCT_CODE_INDEX:
return record.getProductCode();
case PRODUCT_PRICE_INDEX:
return record.getProductPrice();
case PRODUCT_TOTAL_AMOUNT_INDEX:
return record.getProductTotalAmount();
case PRODUCT_QUANTITY_INDEX:
return record.getProductQuantity();
default:
return new Object();
}
}
//setting data in a cell
@Override
public void setValueAt(Object value, int row, int column) {
Product record = (Product)dataVector.get(row);
switch (column) {
case PRODUCT_DESCRIPTION_INDEX:
record.setProductDescription((String)value);
break;
case PRODUCT_CODE_INDEX:
record.setProductCode((String)value);
break;
case PRODUCT_PRICE_INDEX:
record.setProductPrice((Double)value);
break;
case PRODUCT_QUANTITY_INDEX:
record.setProductQuantity((Integer)value);
break;
case PRODUCT_TOTAL_AMOUNT_INDEX:
record.setProductTotalAmount((Double)value);
break;
default:
System.out.println("invalid index");
}
fireTableCellUpdated(row, column);//this is for updating the cell
}
public boolean hasEmptyRow() {
if (dataVector.isEmpty()) return false;
Product productRecord = (Product)dataVector.get(dataVector.size() - 1);
if (productRecord.getProductDescription().trim().equals("") &&
productRecord.getProductCode().trim().equals("") &&
(productRecord.getProductPrice()!=1 &&
productRecord.getProductQuantity()!=1 &&
productRecord.getProductTotalAmount()!=1))
{
return true;
}
else return false;
}
public void addEmptyRow() {
dataVector.add(new Product());
fireTableRowsInserted(
dataVector.size() - 1,
dataVector.size() - 1);
}}
最后的class如下:
public final class InteractiveForm extends JPanel {
public static final String[] columnNames = {
"Quantity", "Product Code", "Product Description", "Product Price","Product Quantity",""
};
protected JTable table;
protected JScrollPane scroller;
protected InteractiveTableModel tableModel;
public InteractiveForm() {
initComponent();
}
public void initComponent() {
tableModel = new InteractiveTableModel(columnNames);
tableModel.addTableModelListener(new InteractiveForm.InteractiveTableModelListener());
table = new JTable();
table.setModel(tableModel);
table.setSurrendersFocusOnKeystroke(true);
if (!tableModel.hasEmptyRow()) {
tableModel.addEmptyRow();
}
scroller = new javax.swing.JScrollPane(table);
table.setPreferredScrollableViewportSize(new java.awt.Dimension(500, 300));
TableColumn hidden = table.getColumnModel().getColumn(InteractiveTableModel.HIDDEN_INDEX);
hidden.setMinWidth(2);
hidden.setPreferredWidth(2);
hidden.setMaxWidth(2);
hidden.setCellRenderer(new InteractiveRenderer(InteractiveTableModel.HIDDEN_INDEX));
setLayout(new BorderLayout());
add(scroller, BorderLayout.CENTER);
}
public void highlightLastRow(int row) {
int lastrow = tableModel.getRowCount();//return the number of rows - last row
if (row == lastrow - 1) {
table.setRowSelectionInterval(lastrow - 1, lastrow - 1);
} else {
table.setRowSelectionInterval(row + 1, row + 1);
}
table.setColumnSelectionInterval(0, 0);//setting it to the first column
}
class InteractiveRenderer extends DefaultTableCellRenderer {
protected int interactiveColumn;
public InteractiveRenderer(int interactiveColumn) {
this.interactiveColumn = interactiveColumn;
}
@Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column)
{
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (column == interactiveColumn && hasFocus) {
if ((InteractiveForm.this.tableModel.getRowCount() - 1) == row &&
!InteractiveForm.this.tableModel.hasEmptyRow())
{
InteractiveForm.this.tableModel.addEmptyRow();//this is where we append a new row
}
highlightLastRow(row);//making it get focus and highlighted
}
return c;//returning the component
}
}
public class InteractiveTableModelListener implements TableModelListener {
@Override
public void tableChanged(TableModelEvent evt) {
if (evt.getType() == TableModelEvent.UPDATE) {
int column = evt.getColumn();
int row = evt.getFirstRow();
System.out.println("row: " + row + " column: " + column);
table.setColumnSelectionInterval(column + 1, column + 1);
table.setRowSelectionInterval(row, row);
}
}
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
JFrame frame = new JFrame("Interactive Form");
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
frame.getContentPane().add(new InteractiveForm());
frame.pack();
frame.setVisible(true);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException | HeadlessException e) {
}
}}
我与上面 link 上的 table 的唯一区别是添加了我自己的变量,这些变量不应该 work.The 逻辑在逃避我这里。
如何让我的 table 的行为方式与 this one 相同?
您正在设置空值。它适用于 Double.class 而不是 double.class。
public class Product {
String productDescription;
String productCode;
Double productPrice;
//dummy properties
Double productQuantity;
Double productTotalAmount;
..
@Override
public Class getColumnClass(int column) {// returns a class representing the datatype of the data stored in that column
switch (column) {
case PRODUCT_DESCRIPTION_INDEX:
case PRODUCT_CODE_INDEX:
return String.class;
case PRODUCT_QUANTITY_INDEX:
case PRODUCT_PRICE_INDEX:
case PRODUCT_TOTAL_AMOUNT_INDEX:
return Double.class;
default:
return Object.class;
}
}
...
更新 与 boxing/unboxing 有关的东西。我知道该由谁来修复它,但我仍然不知道真正的原因。