JTable单元格更新不起作用。

9
在我的应用程序中,我使用了一个类似于这个例子的java表格。我的问题是,当我更改单元格的值(甚至在上面的例子中),数据模型直到我点击不同的单元格之前都不会得到更新。即使我在更改单元格值后点击表格下面的灰色区域,模型也不会改变。我认为原因是单元格保持焦点,直到我点击另一个单元格。如何避免这种情况,并在不点击表格单元格的情况下更新模型。提前致谢。
我已经编辑了上面的示例代码以反映问题。
public class JTableDemo extends JApplet {
  private JTextArea txt = new JTextArea(4, 20);

  // The TableModel controls all the data:
  class DataModel extends AbstractTableModel {
    Object[][] data = { { "one", "two", "three", "four" },
        { "five", "six", "seven", "eight" },
        { "nine", "ten", "eleven", "twelve" }, };

    // Prints data when table changes:
    class TML implements TableModelListener {
      public void tableChanged(TableModelEvent e) {
        txt.setText(""); // Clear it
        for (int i = 0; i < data.length; i++) {
          for (int j = 0; j < data[0].length; j++)
            txt.append(data[i][j] + " ");
          txt.append("\n");
        }
      }
    }

    public DataModel() {
      addTableModelListener(new TML());
       fireTableDataChanged();
    }

    public int getColumnCount() {
      return data[0].length;
    }

    public int getRowCount() {
      return data.length;
    }

    public Object getValueAt(int row, int col) {
      return data[row][col];
    }

    public void setValueAt(Object val, int row, int col) {
      data[row][col] = val;
      System.out.println(val);
      // Indicate the change has happened:
      fireTableDataChanged();
    }

    public boolean isCellEditable(int row, int col) {
      return true;
    }
  }

  public void init() {
    Container cp = getContentPane();
    JTable table = new JTable(new DataModel());
    cp.add(new JScrollPane(table));
    cp.add(BorderLayout.SOUTH, txt);
  }

  public static void main(String[] args) {
    run(new JTableDemo(), 350, 200);
  }

  public static void run(JApplet applet, int width, int height) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(applet);
    frame.setSize(width, height);
    applet.init();
    applet.start();
    frame.setVisible(true);
  }
}

当您运行示例时,可以在表格下方的文本区域中查看表格内容。当您更新单元格后,它应该出现在下面的文本框中。但是,在您单击其他单元格之前,“setValueAt”方法不会被调用。

1
你能否编辑你的问题并添加一个展示问题的 SSCCE 呢? - Bart Kiers
3个回答

14

这是预期行为:编辑的值直到显式用户操作(例如按下回车键、切换标签或单击表格外的其他地方)才会提交到后备模型中...

JTable 的一个奇怪之处(有些人称其为错误 :-))是默认情况下,当焦点转移到表格的“外部”时,编辑不会被终止。要强制它这样做,您需要进行配置:

  table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

顺便说一句(无关紧要,只是为了保持清醒):始终触发最精细的事件类型,这里应该是cellUpdated而不是dataChanged。


我喜欢你的回答,非常有价值,加一。 - mKorbel

8

默认的更新机制只有在单元格编辑器失去焦点时才会更改模型。要么通过按Tab键离开单元格,要么点击其他单元格将导致重要的“失去焦点”事件,从而触发模型更改。

点击灰色区域没有任何效果,因为那里没有活动元素可以处理事件 - 表格忽略了该点击事件。

如果你想改变这个行为,你需要找到一个事件,告诉计算机用户“完成编辑”。你怎么知道呢?

[编辑] 请注意,添加一个“保存”按钮可能会打扰用户的编辑“流程”(点击单元格,进行编辑,抓住鼠标,瞄准,点击保存,点击下一个单元格,回到键盘,进行编辑...)


只需添加一个标签为“保存”的按钮,不做任何操作即可。除了kleopatra的答案中提到的客户端属性外,这应该就可以解决问题了。请注意,这将极大地困扰您的用户,因为它意味着需要额外的鼠标点击。尝试添加一个助记符,以便人们可以使用“Alt+S”停止编辑(无需离开键盘)。 - Aaron Digulla
1
默认情况下,失去焦点不会触发提交(这是一个长期存在的问题,直到他们被说服至少支持可选设置该行为)。实际上,通过单击或切换到另一个单元格进行提交是由tableUI代码显式触发的。 - kleopatra
@Kleo:您可能错过了“kleopatra答案中的客户端属性”。如果这不起作用,则需要在按钮的单击处理程序中调用单元格编辑器的fireEditingStopped() - Aaron Digulla
2
我是这样做的。当用户在保存操作监听器中单击保存按钮时,我调用jTable.getCellEditor().stopCellEditing()方法。 - nath
哈哈...确实,我错过了那一行 :-) - 瞎了我的眼 - 没有读到注释) 我坚持要说的是不要通过切换/点击另一个单元格来关联焦点和提交,它们是不同的东西。而且,你从来不会从编辑器外部的某个处理程序中调用fireEditingStopped(而是调用stopCellEditing)。事实上,永远不要从定义它的类之外的任何地方调用任何fireXX...(五分钟的编辑限制迫使我删除旧的并编写新的评论;-) - kleopatra
1
请注意,如果没有正在进行的编辑,则jTable.getCellEditor()将返回null。 - James Wierzba

0

对于我的情况

public void setValueAt(Object val, int row, int col) {
  data[row][col] = val;
  System.out.println(val);
  // Indicate the change has happened:
  fireTableDataChanged();
}

你需要同时调用父类函数。

public void setValueAt(Object val, int row, int col) {
  data[row][col] = val;
  System.out.println(val);
  // Indicate the change has happened:
  fireTableDataChanged();
  super.setValueAt(val,row,col);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接