在 TableModel 更改中保留 JTable 选择

发布于 2024-07-08 09:19:47 字数 415 浏览 6 评论 0原文

当我们从 TableModel 执行 fireTableDataChanged()fireTableRowsUpdated() 时,我们会看到 JTable 选择被清除。

这是预期的结果,还是我们做错了什么? 我在 JTable (或其他相关类)上没有看到任何有关清除/保留模型更新选择的属性。

如果这是默认行为,有没有好的方法来防止这种情况? 也许有某种方法可以在更新之前“锁定”选择并在更新之后解锁?

开发人员一直在尝试保存更新前的选择并重新应用。 有点慢。

如果重要的话,这是 Windows XP 上的 Java 1.4.2。 根据我们使用的某些供应商代码,我们仅限于该版本。

We're seeing JTable selection get cleared when we do a fireTableDataChanged() or fireTableRowsUpdated() from the TableModel.

Is this expected, or are we doing something wrong? I didn't see any property on the JTable (or other related classes) about clearing/preserving selection on model updates.

If this is default behavior, is there a good way to prevent this? Maybe some way to "lock" the selection before the update and unlock after?

The developer has been experimenting with saving the selection before the update and re-applying it. It's a little slow.

This is Java 1.4.2 on Windows XP, if that matters. We're limited to that version based on some vendor code we use.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

忘年祭陌 2024-07-15 09:19:48

我遇到了同样的问题,当我试图搜索我得到这个问题的原因时,但这似乎是 Java SDK 中的一个错误。 https://bugs.java.com/bugdatabase/view_bug?bug_id=4276786

解决方法

可以使用临时解决方法。 一旦修复了这个错误,就应该将其删除,因为它的适用性尚未针对固定版本进行测试。

使用 JTable 的这个子类。

注意:这适用于 MetalLookAndFeel。 如果使用其他外观,则内部 FixTableUI 子类必须扩展 TableUI 子类以获得该外观。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
import javax.swing.plaf.basic.*;

public class FixedTable extends JTable {

  private boolean isControlDownInDrag;

  public FixedTable(TableModel model) {
      super(model);
      setUI(new FixedTableUI());
  }

  private class FixedTableUI extends BasicTableUI {
      private MouseInputHandler handler = new MouseInputHandler() {
          public void mouseDragged(MouseEvent e) {
              if (e.isControlDown()) {
                  isControlDownInDrag = true;
              }
              super.mouseDragged(e);
          }

          public void mousePressed(MouseEvent e) {
              isControlDownInDrag = false;
              super.mousePressed(e);
          }

          public void mouseReleased(MouseEvent e) {
              isControlDownInDrag = false;
              super.mouseReleased(e);
          }
      };

      protected MouseInputListener createMouseInputListener() {
          return handler;
      }
  }

  public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
      if (isControlDownInDrag) {
          ListSelectionModel rsm = getSelectionModel();
          ListSelectionModel csm = getColumnModel().getSelectionModel();

          int anchorRow = rsm.getAnchorSelectionIndex();
          int anchorCol = csm.getAnchorSelectionIndex();

          boolean anchorSelected = isCellSelected(anchorRow, anchorCol);

          if (anchorSelected) {
              rsm.addSelectionInterval(anchorRow, rowIndex);
              csm.addSelectionInterval(anchorCol, columnIndex);
          } else {
              rsm.removeSelectionInterval(anchorRow, rowIndex);
              csm.removeSelectionInterval(anchorCol, columnIndex);
          }

          if (getAutoscrolls()) {
              Rectangle cellRect = getCellRect(rowIndex, columnIndex, false);
              if (cellRect != null) {
                  scrollRectToVisible(cellRect);
              }
          }
      } else {
          super.changeSelection(rowIndex, columnIndex, toggle, extend);
      }
  }
}

注意行屈膝礼http://bugs.sun.com

I was facing same issue and when tried to search the reason I got this question but it seems a bug in Java SDK. https://bugs.java.com/bugdatabase/view_bug?bug_id=4276786

WORK AROUND

A temporary work-around is available. It should be removed once this bug is fixed as it's suitability has NOT been tested against fixed releases.

Use this subclass of JTable.

Note: This is for the MetalLookAndFeel. If using other look and feels, the inner FixedTableUI subclass will have to extend the TableUI subclass for that look and feel.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
import javax.swing.plaf.basic.*;

public class FixedTable extends JTable {

  private boolean isControlDownInDrag;

  public FixedTable(TableModel model) {
      super(model);
      setUI(new FixedTableUI());
  }

  private class FixedTableUI extends BasicTableUI {
      private MouseInputHandler handler = new MouseInputHandler() {
          public void mouseDragged(MouseEvent e) {
              if (e.isControlDown()) {
                  isControlDownInDrag = true;
              }
              super.mouseDragged(e);
          }

          public void mousePressed(MouseEvent e) {
              isControlDownInDrag = false;
              super.mousePressed(e);
          }

          public void mouseReleased(MouseEvent e) {
              isControlDownInDrag = false;
              super.mouseReleased(e);
          }
      };

      protected MouseInputListener createMouseInputListener() {
          return handler;
      }
  }

  public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
      if (isControlDownInDrag) {
          ListSelectionModel rsm = getSelectionModel();
          ListSelectionModel csm = getColumnModel().getSelectionModel();

          int anchorRow = rsm.getAnchorSelectionIndex();
          int anchorCol = csm.getAnchorSelectionIndex();

          boolean anchorSelected = isCellSelected(anchorRow, anchorCol);

          if (anchorSelected) {
              rsm.addSelectionInterval(anchorRow, rowIndex);
              csm.addSelectionInterval(anchorCol, columnIndex);
          } else {
              rsm.removeSelectionInterval(anchorRow, rowIndex);
              csm.removeSelectionInterval(anchorCol, columnIndex);
          }

          if (getAutoscrolls()) {
              Rectangle cellRect = getCellRect(rowIndex, columnIndex, false);
              if (cellRect != null) {
                  scrollRectToVisible(cellRect);
              }
          }
      } else {
          super.changeSelection(rowIndex, columnIndex, toggle, extend);
      }
  }
}

Note Curtsey to http://bugs.sun.com

燕归巢 2024-07-15 09:19:47

您需要保留选择然后重新应用它。

首先,您需要获取所有选定单元格的列表。

然后,当您使用新数据重新加载 JTable 时,您需要以编程方式重新应用这些相同的选择。

我想说的另一点是,如果表中的行数或列数在每次表模型重新加载后增加或减少,那么请不要费心保留选择。

在模型更新之前,用户可以选择值为“Duck”的第 2 行第 1 列。 但模型更新后,相同的数据现在可以出现在第 4 行第 1 列中,并且原始单元格第 2 行第 1 列可能包含新数据,例如“Pig”。 现在,如果您强制将选择设置为模型更新之前的选择,这可能不是用户想要的。

因此,以编程方式选择单元格可能是一把双刃剑。 如果您不确定,请不要这样做。

You need to preserve the selection and then re-apply it.

First of all you will need to get a list of all the selected cells.

Then when you re-load the JTable with the new data you need to programmatically re-apply those same selections.

The other point I want to make is, if the number or rows or columns in your table are increasing or decreasing after each table model reload, then please don't bother preserving the selection.

The user could have selected row 2 column 1 having a value say "Duck", before model updation. But after model updation that same data can now occur in row 4 column 1, and your original cell row 2 column 1 could have new data such as "Pig". Now if you forcibly set the selection to what it was before the model updation, this may not be what the user wanted.

So programmatically selecting cells could be a double edged sword. Don't do it, if you are not sure.

阳光①夏 2024-07-15 09:19:47

如果该表的结构未更改(即,如果您没有添加/删除任何列/行),则可以自动保留该表的选择,如下所示。

如果您编写了自己的 TableModel 实现,则可以简单地重写 fireTableDataChanged() 方法:

@Override
public void fireTableDataChanged() {
    fireTableChanged(new TableModelEvent(this, //tableModel
        0, //firstRow
        getRowCount() - 1, //lastRow 
        TableModelEvent.ALL_COLUMNS, //column 
        TableModelEvent.UPDATE)); //changeType
}

这应该确保在仅更改数据而不是表结构的情况下保留您的选择。 此方法与未重写此方法时调用的方法之间的唯一区别在于,为 lastRow 参数传递 getRowCount() - 1 而不是 Integer.MAX_VALUE,后者充当一个指示符,不仅具有所有表中的数据发生了变化,但行数也可能发生变化。

You can automatically preserve a table's selection if the STRUCTURE of that table hasn't changed (i.e. if you haven't add/removed any columns/rows) as follows.

If you've written your own implementation of TableModel, you can simply override the fireTableDataChanged() method:

@Override
public void fireTableDataChanged() {
    fireTableChanged(new TableModelEvent(this, //tableModel
        0, //firstRow
        getRowCount() - 1, //lastRow 
        TableModelEvent.ALL_COLUMNS, //column 
        TableModelEvent.UPDATE)); //changeType
}

and this should ensure that your selection is maintained provided that only the data and not the structure of the table has changed. The only difference between this, and what would be called if this method weren't overridden is that getRowCount() - 1 is passed for the lastRow argument instead of Integer.MAX_VALUE, the latter of which acts a signifier that not only has all the data in the table changed but that the number of rows may have as well.

笑脸一如从前 2024-07-15 09:19:47

我在应用程序中遇到了同样的问题。 就我而言,表中的模型是对象列表,其中对象属性映射到列。 在这种情况下,当修改列表时,我检索选定的索引并存储更新列表之前选择的对象。 在修改列表之后和更新表格之前,我将计算所选对象的位置。 如果修改后它仍然存在,那么我会将选择设置为新索引。

修改后仅设置表中选定的索引是行不通的,因为对象可能会改变列表中的位置。

附带说明一下,我发现使用 GlazedLists 可以让处理表格时变得更加轻松。

I had the same issue in an application. In my case the model in the table was a list of objects, where the object properties where mapped to columns. In that case, when the list was modified, I retrieved the selected index and stored the object that was selected before updating the list. After the list is modified and before the table is updated, I would calculate the position of the selected object. If it was still present after the modification, then I would set the selection to the new index.

Just setting the selected index in the table after the modification will not work, because the object may change position in the list.

As a side note, I found that working with GlazedLists makes life much easier when dealing with tables.

注定孤独终老 2024-07-15 09:19:47

这是默认行为。 如果您调用 fireTableDataChanged(),当您设置全新模型时,整个表将从头开始重建。 在这种情况下,选择自然就失去了。 如果您调用 fireTableRowsUpdated(),一般情况下选择也会被清除。 唯一的方法是记住选择然后进行设置。 不幸的是,我们不能保证选择仍然有效。 恢复选择时要小心。

This is default behavior. If you call fireTableDataChanged() the entire table is rebuild from scratch as you set entirely new model. In this case the selection is, naturally, lost. If you call fireTableRowsUpdated() the selection is also cleared in general cases. The only way is to remember selection and then set this. Unfortunately there is no guarantee that the selection will be still valid. Be careful if restoring selection.

那小子欠揍 2024-07-15 09:19:47

作为参考,正如 @Swapnonil Mukherjee 所说,这对具有可选择行的表起到了作用:

// preserve selection calling fireTableDataChanged()
final int[] sel = table.getSelectedRows();

fireTableDataChanged();

for (int i=0; i<sel.length; i++)
    table.getSelectionModel().addSelectionInterval(sel[i], sel[i]);

for reference, as @Swapnonil Mukherjee stated, this did the trick with a table with selectable rows:

// preserve selection calling fireTableDataChanged()
final int[] sel = table.getSelectedRows();

fireTableDataChanged();

for (int i=0; i<sel.length; i++)
    table.getSelectionModel().addSelectionInterval(sel[i], sel[i]);
小鸟爱天空丶 2024-07-15 09:19:47

如果我没记错的话,保存选择并重新应用它也是我们所做的......

If I recall correctly, saving selection and re-applying it is what we have done too...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文