英文:
How do I dynamically add rows in custom DefaultTableModel?
问题
我已经理解您只需要代码部分的中文翻译。以下是您提供的代码的中文翻译:
我已经子类化了`DefaultTableModel`,以便我的表格可以显示存储在JComboBox中的枚举值。只要我在构造函数中立即向这个表格添加数据,这就可以正常工作。让我向您展示我的意思。
final List<PageLabel> pageLabels = new ArrayList<>();
// 添加一些数据
pageLabels.add(....);
final PageLabelTableModel tableModel = new PageLabelTableModel(pageLabels);
问题是我的应用程序从某个文本文件获取数据。只有在处理此文件时,应用程序才应该将数据添加到表格中。换句话说,我希望能够动态添加行到表格中。从理论上讲,这应该很容易。然而,在实践中并非如此。我编写了一个简单的方法来填充表格:
public void fillTable(final List<PageLabel> labels) {
for (final PageLabel label : labels) {
final Vector<PageLabel> rowData = new Vector<>();
rowData.add(label);
tableModel.addRow(rowData);
}
}
它会引发异常:
线程“Thread-1”中的异常"java.lang.ArrayIndexOutOfBoundsException: 0 >= 0
at java.base/java.util.Vector.elementAt(Vector.java:466)
at java.desktop/javax.swing.table.DefaultTableModel.justifyRows(DefaultTableModel.java:278)
at java.desktop/javax.swing.table.DefaultTableModel.insertRow(DefaultTableModel.java:382)
at java.desktop/javax.swing.table.DefaultTableModel.addRow(DefaultTableModel.java:357)
为什么会这样?这是我的自定义表格模型类:
// 该代码在很大程度上借鉴自
// https://www.codejava.net/java-se/swing/how-to-create-jcombobox-cell-editor-for-jtable
public class PageLabelTableModel extends DefaultTableModel {
private static final String[] columnNames = { "标签", "起始页", "样式" };
private final List<PageLabel> pageLabels = new ArrayList<>();
public PageLabelTableModel() {
super(columnNames, 0);
}
@Override
public Class<?> getColumnClass(final int column) {
return getValueAt(0, column).getClass();
}
@Override
public int getRowCount() {
return pageLabels != null ? pageLabels.size() : 0;
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public String getColumnName(final int column) {
return columnNames[column];
}
@Override
public boolean isCellEditable(final int rowIndex, final int columnIndex) {
return columnIndex >= 0;
}
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
final PageLabel pageLabel = pageLabels.get(rowIndex);
final Object returnValue;
switch (columnIndex) {
case 0 -> returnValue = pageLabel.label();
case 1 -> returnValue = pageLabel.startPage();
case 2 -> returnValue = pageLabel.style();
default -> throw new IllegalArgumentException(
String.format("找不到列索引 %d!", columnIndex)
);
}
return returnValue;
}
@Override
public void setValueAt(final Object value, final int rowIndex, final int columnIndex) {
final PageLabel pageLabel = pageLabels.get(rowIndex);
switch (columnIndex) {
case 0 -> pageLabel.label((String) value);
case 1 -> pageLabel.startPage((int) value);
case 2 -> pageLabel.style((LabelStyle) value);
default -> throw new IllegalArgumentException(
String.format("找不到列索引 %d!", columnIndex)
);
}
}
}
根据@Abra的评论,我提供了PageLabel类的代码:
public class PageLabel {
private String label;
private int startPage;
private LabelStyle style;
// 无参数构造函数、全参数构造函数、getter和setter
// 省略以简洁为目的
}
以下是上述类使用的枚举:
public enum LabelStyle {
DECIMAL("十进制"),
ROMAN_LOWER("小写罗马数字"),
ROMAN_UPPER("大写罗马数字"),
LETTER_UPPPER("大写字母"),
LETTER_LOWER("小写字母"),
NONE("无");
private final String style;
LabelStyle(final String style) {
this.style = style;
}
}
英文:
I have subclassed DefaultTableModel
so that my table could show values from enum stored in JComboBox. This works fine provided that I add immediately data to this table in constructor. Let me show you what I mean by that.
final List<PageLabel> pageLabels = new ArrayList<>();
// add some data
pageLabels.add(....);
final PageLabelTableModel tableModel = new PageLabelTableModel(pageLabels);
The thing is my application gets data from some text file. Only when this file is processed, the application should add the data the table. So, in other words, I'd like to dynamically add rows to the table. In theory it should be easy. In practice, however, it is not. I wrote a simple method to fill the table:
public void fillTable(final List<PageLabel> labels) {
for (final PageLabel label : labels) {
final Vector<PageLabel> rowData = new Vector<>();
rowData.add(label);
tableModel.addRow(rowData);
}
}
It fails with the exception:
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 0 >= 0
at java.base/java.util.Vector.elementAt(Vector.java:466)
at java.desktop/javax.swing.table.DefaultTableModel.justifyRows(DefaultTableModel.java:278)
at java.desktop/javax.swing.table.DefaultTableModel.insertRow(DefaultTableModel.java:382)
at java.desktop/javax.swing.table.DefaultTableModel.addRow(DefaultTableModel.java:357)
Why is that? Here's my custom table model class:
// the code heavily borrows from
// https://www.codejava.net/java-se/swing/how-to-create-jcombobox-cell-editor-for-jtable
public class PageLabelTableModel extends DefaultTableModel {
private static final String[] columnNames = { "Label", "Start page", "Style" };
private final List<PageLabel> pageLabels = new ArrayList<>();
public PageLabelTableModel() {
super(columnNames, 0);
}
@Override
public Class<?> getColumnClass(final int column) {
return getValueAt(0, column).getClass();
}
@Override
public int getRowCount() {
return pageLabels != null ? pageLabels.size() : 0;
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public String getColumnName(final int column) {
return columnNames[column];
}
@Override
public boolean isCellEditable(final int rowIndex, final int columnIndex) {
return columnIndex >= 0;
}
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
final PageLabel pageLabel = pageLabels.get(rowIndex);
final Object returnValue;
switch (columnIndex) {
case 0 -> returnValue = pageLabel.label();
case 1 -> returnValue = pageLabel.startPage();
case 2 -> returnValue = pageLabel.style();
default -> throw new IllegalArgumentException(
String.format("Column index %d not found!", columnIndex)
);
}
return returnValue;
}
@Override
public void setValueAt(final Object value, final int rowIndex, final int columnIndex) {
final PageLabel pageLabel = pageLabels.get(rowIndex);
switch (columnIndex) {
case 0 -> pageLabel.label((String) value);
case 1 -> pageLabel.startPage((int) value);
case 2 -> pageLabel.style((LabelStyle) value);
default -> throw new IllegalArgumentException(
String.format("Column index %d not found!", columnIndex)
);
}
}
}
As per comment by @Abra I provide the code for PageLabel class:
public class PageLabel {
private String label;
private int startPage;
private LabelStyle style;
// no-args, all-args, getters/setters
// omitted for brevity
}
Below is enum used by the above-mentioned class:
public enum LabelStyle {
DECIMAL("Decimal"),
ROMAN_LOWER("Roman lower"),
ROMAN_UPPER("Roman upper"),
LETTER_UPPPER("Letters upper"),
LETTER_LOWER("Letters lower"),
NONE("None");
private final String style;
LabelStyle(final String style) {
this.style = style;
}
}
答案1
得分: 0
希望您能在对 PageLabelTableModel
类进行最小程度的重构的基础上继续构建。
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
public class PageLabelTableModel extends AbstractTableModel {
private static final String[] columnNames = { "标签", "起始页", "样式" };
private final List<PageLabel> pageLabels;
public PageLabelTableModel() {
this(new ArrayList<>());
}
public PageLabelTableModel(List<PageLabel> labels) {
pageLabels = labels;
}
public void addRow(PageLabel label) {
pageLabels.add(label);
int count = getRowCount();
fireTableRowsInserted(count, count);
}
@Override
public int getRowCount() {
return pageLabels == null ? 0 : pageLabels.size();
}
public Class<?> getColumnClass(int columnIndex) {
Class<?> theClass;
switch (columnIndex) {
case 0:
theClass = String.class;
break;
case 1:
theClass = Number.class;
break;
case 2:
theClass = LabelStyle.class;
break;
default:
theClass = Object.class;
}
return theClass;
}
@Override
public int getColumnCount() {
return columnNames.length;
}
public String getColumnName(int column) {
return columnNames[column];
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
Object value;
PageLabel pageLabel = pageLabels.get(rowIndex);
switch (columnIndex) {
case 0:
value = pageLabel.getLabel();
break;
case 1:
value = Integer.valueOf(pageLabel.getStartPage());
break;
case 2:
value = pageLabel.getStyle();
break;
default:
value = null;
}
return value;
}
@Override
public boolean isCellEditable(final int rowIndex, final int columnIndex) {
return columnIndex >= 0;
}
}
继承 AbstractTableModel
类的类必须实现以下三个方法:
getRowCount
getColumnCount
getValueAt
还建议重写其他 AbstractTableModel
方法,包括:
getColumnName
(查看AbstractTableModel
类中该方法的代码以了解原因。)getColumnClass
(为了使用默认的渲染器。有关更多详细信息,请参阅如何使用表格。)isCellEditable
(不过您已经重写了这个方法。)
请注意,在 addRow
方法中,调用了 fireTableRowsInserted
。这是为了通知 JTable
类数据模型的数据已更改。再次参考 如何使用表格 以获取更多详细信息。
此外,我还添加了一个 toString
方法给 LabelStyle
:
public enum LabelStyle {
DECIMAL("十进制"),
ROMAN_LOWER("小写罗马数字"),
ROMAN_UPPER("大写罗马数字"),
LETTER_UPPPER("大写字母"),
LETTER_LOWER("小写字母"),
NONE("无");
private final String style;
LabelStyle(final String style) {
this.style = style;
}
public String toString() {
return style;
}
}
英文:
Hopefully you can build on this minimal refactor of your PageLabelTableModel
class.
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
public class PageLabelTableModel extends AbstractTableModel {
private static final String[] columnNames = { "Label", "Start page", "Style" };
private final List<PageLabel> pageLabels;
public PageLabelTableModel() {
this(new ArrayList<>());
}
public PageLabelTableModel(List<PageLabel> labels) {
pageLabels = labels;
}
public void addRow(PageLabel label) {
pageLabels.add(label);
int count = getRowCount();
fireTableRowsInserted(count, count);
}
@Override
public int getRowCount() {
return pageLabels == null ? 0 : pageLabels.size();
}
public Class<?> getColumnClass(int columnIndex) {
Class<?> theClass;
switch (columnIndex) {
case 0:
theClass = String.class;
break;
case 1:
theClass = Number.class;
break;
case 2:
theClass = LabelStyle.class;
break;
default:
theClass = Object.class;
}
return theClass;
}
@Override
public int getColumnCount() {
return columnNames.length;
}
public String getColumnName(int column) {
return columnNames[column];
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
Object value;
PageLabel pageLabel = pageLabels.get(rowIndex);
switch (columnIndex) {
case 0:
value = pageLabel.getLabel();
break;
case 1:
value = Integer.valueOf(pageLabel.getStartPage());
break;
case 2:
value = pageLabel.getStyle();
break;
default:
value = null;
}
return value;
}
@Override
public boolean isCellEditable(final int rowIndex, final int columnIndex) {
return columnIndex >= 0;
}
}
- Classes that extend
AbstractTableModel
must implement the following three methods:getRowCount
getColumnCount
getValueAt
- It is also recommended to override other
AbstractTableModel
methods, including:getColumnName
(Look at code of that method in classAbstractTableModel
to understand why.)getColumnClass
(To make use of the default renderers. Refer to How to Use Tables for more details.)isCellEditable
(But you have already overridden this.)
- Note in the
addRow
method, the call tofireTableRowsInserted
. This is to notify theJTable
class that the data in the model has changed. Again, refer to How to Use Tables for more details.
Also, I added a toString
method to LabelStyle
:
public enum LabelStyle {
DECIMAL("Decimal"),
ROMAN_LOWER("Roman lower"),
ROMAN_UPPER("Roman upper"),
LETTER_UPPPER("Letters upper"),
LETTER_LOWER("Letters lower"),
NONE("None");
private final String style;
LabelStyle(final String style) {
this.style = style;
}
public String toString() {
return style;
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论