如何在自定义DefaultTableModel中动态添加行?

huangapple go评论91阅读模式
英文:

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&lt;PageLabel&gt; pageLabels = new ArrayList&lt;&gt;();
// 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&lt;PageLabel&gt; labels) {
for (final PageLabel label : labels) {
final Vector&lt;PageLabel&gt; rowData = new Vector&lt;&gt;();
rowData.add(label);
tableModel.addRow(rowData);
}
}

It fails with the exception:

Exception in thread &quot;Thread-1&quot; java.lang.ArrayIndexOutOfBoundsException: 0 &gt;= 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 = { &quot;Label&quot;, &quot;Start page&quot;, &quot;Style&quot; };
private final List&lt;PageLabel&gt; pageLabels = new ArrayList&lt;&gt;();
public PageLabelTableModel() {
super(columnNames, 0);
}
@Override
public Class&lt;?&gt; 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 &gt;= 0;
}
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
final PageLabel pageLabel = pageLabels.get(rowIndex);
final Object returnValue;
switch (columnIndex) {
case 0 -&gt; returnValue = pageLabel.label();
case 1 -&gt; returnValue = pageLabel.startPage();
case 2 -&gt; returnValue = pageLabel.style();
default -&gt; throw new IllegalArgumentException(
String.format(&quot;Column index %d not found!&quot;, 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 -&gt; pageLabel.label((String) value);
case 1 -&gt; pageLabel.startPage((int) value);
case 2 -&gt; pageLabel.style((LabelStyle) value);
default -&gt; throw new IllegalArgumentException(
String.format(&quot;Column index %d not found!&quot;, 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(&quot;Decimal&quot;),
ROMAN_LOWER(&quot;Roman lower&quot;),
ROMAN_UPPER(&quot;Roman upper&quot;),
LETTER_UPPPER(&quot;Letters upper&quot;),
LETTER_LOWER(&quot;Letters lower&quot;),
NONE(&quot;None&quot;);
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 类的类必须实现以下三个方法:

  1. getRowCount
  2. getColumnCount
  3. getValueAt

还建议重写其他 AbstractTableModel 方法,包括:

  1. getColumnName
    (查看 AbstractTableModel 类中该方法的代码以了解原因。)
  2. getColumnClass
    (为了使用默认的渲染器。有关更多详细信息,请参阅如何使用表格。)
  3. 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 = { &quot;Label&quot;, &quot;Start page&quot;, &quot;Style&quot; };
    private final List&lt;PageLabel&gt; pageLabels;

    public PageLabelTableModel() {
        this(new ArrayList&lt;&gt;());
    }

    public PageLabelTableModel(List&lt;PageLabel&gt; 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&lt;?&gt; getColumnClass(int columnIndex) {
        Class&lt;?&gt; 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 &gt;= 0;
    }
}
  • Classes that extend AbstractTableModel must implement the following three methods:
    1. getRowCount
    2. getColumnCount
    3. getValueAt
  • It is also recommended to override other AbstractTableModel methods, including:
    1. getColumnName
      (Look at code of that method in class AbstractTableModel to understand why.)
    2. getColumnClass
      (To make use of the default renderers. Refer to How to Use Tables for more details.)
    3. isCellEditable
      (But you have already overridden this.)
  • Note in the addRow method, the call to fireTableRowsInserted. This is to notify the JTable 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(&quot;Decimal&quot;),
    ROMAN_LOWER(&quot;Roman lower&quot;),
    ROMAN_UPPER(&quot;Roman upper&quot;),
    LETTER_UPPPER(&quot;Letters upper&quot;),
    LETTER_LOWER(&quot;Letters lower&quot;),
    NONE(&quot;None&quot;);

    private final String style;

    LabelStyle(final String style) {
       this.style = style;
    }

    public String toString() {
        return style;
    }
}

huangapple
  • 本文由 发表于 2023年7月23日 21:34:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76748519.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定