- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
在共享表模型示例中工作时,我意识到如果我们将行过滤器附加到表的行排序器,则此过滤器不会对单元格更新事件产生任何影响。根据RowSorter API :
Concrete implementations of
RowSorter
need to reference a model such asTableModel
orListModel
. The view classes, such asJTable
andJList
, will also have a reference to the model. To avoid ordering dependencies,RowSorter
implementations should not install a listener on the model. Instead the view class will call into theRowSorter
when the model changes. For example, if a row is updated in aTableModel
JTable
invokesrowsUpdated
. When the model changes, the view may call into any of the following methods:modelStructureChanged
,allRowsChanged
,rowsInserted
,rowsDeleted
androwsUpdated
.
根据我对这一段的理解,单元格更新是行更新的一种特殊情况,因此应该调用 rowsUpdated
并相应地过滤行。
为了说明我的意思,请考虑这个简单的过滤器:
private void applyFilter() {
DefaultRowSorter sorter = (DefaultRowSorter)table.getRowSorter();
sorter.setRowFilter(new RowFilter() {
@Override
public boolean include(RowFilter.Entry entry) {
Boolean value = (Boolean)entry.getValue(2);
return value == null || value;
}
});
}
此处第三列应为 Boolean
,如果单元格值为 null
,则必须包含 entry
(行)或 true
。如果我编辑位于第三列的单元格并将其值设置为 false
,那么我希望该行从 View 中“消失”。但是,要完成此操作,我必须再次设置一个新过滤器,因为它似乎无法“自动”工作。
按如下方式将 TableModelListener
附加到模型,我可以看到单元格编辑的更新事件:
model.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.UPDATE) {
int row = e.getLastRow();
int column = e.getColumn();
Object value = ((TableModel)e.getSource()).getValueAt(row, column);
String text = String.format("Update event. Row: %1s Column: %2s Value: %3s", row, column, value);
System.out.println(text);
}
}
});
正如我所说,如果我使用此 TableModelListener
重置过滤器,那么它会按预期工作:
model.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.UPDATE) {
applyFilter();
}
}
});
问题:这是错误/实现问题吗?或者我误解了 API?
这是一个完整的 MCVE说明问题。
import java.awt.BorderLayout;
import javax.swing.BorderFactory;
import javax.swing.DefaultRowSorter;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
public class Demo {
private JTable table;
private void createAndShowGUI() {
DefaultTableModel model = new DefaultTableModel(5, 3) {
@Override
public boolean isCellEditable(int row, int column) {
return column == 2;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 2 ? Boolean.class : super.getColumnClass(columnIndex);
}
};
model.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.UPDATE) {
int row = e.getLastRow();
int column = e.getColumn();
Object value = ((TableModel)e.getSource()).getValueAt(row, column);
String text = String.format("Update event. Row: %1s Column: %2s Value: %3s", row, column, value);
System.out.println(text);
// applyFilter(); un-comment this line to make it work
}
}
});
table = new JTable(model);
table.setAutoCreateRowSorter(true);
applyFilter();
JPanel content = new JPanel(new BorderLayout());
content.setBorder(BorderFactory.createEmptyBorder(8,8,8,8));
content.add(new JScrollPane(table));
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(content);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private void applyFilter() {
DefaultRowSorter sorter = (DefaultRowSorter)table.getRowSorter();
sorter.setRowFilter(new RowFilter() {
@Override
public boolean include(RowFilter.Entry entry) {
Boolean value = (Boolean)entry.getValue(2);
return value == null || value;
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Demo().createAndShowGUI();
}
});
}
}
最佳答案
嗯,在做了一些研究并阅读了 API、错误报告和 Oracle 论坛之后,我发现了一些有趣的东西。
我发现的第一件事是我们必须设置 sortsOnUpdate
属性设置为 true
以便在 rowsUpdated(...) 时启用通知链叫做。否则没有 RowSorterEvent将被解雇, View (我们的 JTable)将不会意识到发生了什么事情并且不会相应地重新绘制。所以做这个小改动:
DefaultRowSorter sorter = (DefaultRowSorter)table.getRowSorter();
sorter.setRowFilter(new RowFilter() {
@Override
public boolean include(RowFilter.Entry entry) {
Boolean value = (Boolean)entry.getValue(2);
return value == null || value;
}
});
sorter.setSortsOnUpdates(true);
我们不必在表模型更新时重新应用过滤器。但是……
当 JTable 实现 RowSorterListener
接口(interface)时,将自己订阅到行排序器作为监听器并处理 RowSorterEvents
。重绘表格时出现错误。这些帖子中很好地描述了奇怪的行为:
简而言之:
当 JTable 处理 RowSorterEvent.TYPE.SORTED
事件时,它只重绘与涉及的行相关的区域,而不是表的其余部分,它保持原样。假设我们编辑了第一行,现在应该对其进行过滤。然后其余的行应该向上移动一行到顶部但事实证明它们不是:只有第一行将被正确地重新绘制以显示第二行但表格的其余部分仍然相同。这实际上是一个错误,因为在这种特殊情况下需要重新绘制整个表。参见 core bug # 6791934
作为解决方法,我们可以将新的 RowSorterListener
附加到 RowSorter
或覆盖 JTable 的 sorterChanged(...)如下所示,以便在我们的 table 上强制进行整个重绘(恕我直言,第二种方法是首选)。
DefaultRowSorter sorter = (DefaultRowSorter)table.getRowSorter();
...
sorter.addRowSorterListener(new RowSorterListener() {
@Override
public void sorterChanged(RowSorterEvent e) {
if (e.getType() == RowSorterEvent.Type.SORTED) {
// We need to call both revalidate() and repaint()
table.revalidate();
table.repaint();
}
}
});
或者
JTable table = new JTable(tableModel) {
@Override
public void sorterChanged(RowSorterEvent e) {
super.sorterChanged(e);
if (e.getType() == RowSorterEvent.Type.SORTED) {
resizeAndRepaint(); // this protected method calls both revalidate() and repaint()
}
}
};
属于 SwingX 的 JXTable 组件库和 JTable 的扩展没有这个问题,因为 SwingLabs 团队已经重写了 sorterChanged(...)
mthod 来破解这个错误:
//----> start hack around core issue 6791934:
// table not updated correctly after updating model
// while having a sorter with filter.
/**
* Overridden to hack around core bug
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791934
*
*/
@Override
public void sorterChanged(RowSorterEvent e) {
super.sorterChanged(e);
postprocessSorterChanged(e);
}
/** flag to indicate if forced revalidate is needed. */
protected boolean forceRevalidate;
/** flag to indicate if a sortOrderChanged has happened between pre- and postProcessModelChange. */
protected boolean filteredRowCountChanged;
/**
* Hack around core issue 6791934: sets flags to force revalidate if appropriate.
* Called before processing the event.
* @param e the TableModelEvent received from the model
*/
protected void preprocessModelChange(TableModelEvent e) {
forceRevalidate = getSortsOnUpdates() && getRowFilter() != null && isUpdate(e) ;
}
/**
* Hack around core issue 6791934: forces a revalidate if appropriate and resets
* internal flags.
* Called after processing the event.
* @param e the TableModelEvent received from the model
*/
protected void postprocessModelChange(TableModelEvent e) {
if (forceRevalidate && filteredRowCountChanged) {
resizeAndRepaint();
}
filteredRowCountChanged = false;
forceRevalidate = false;
}
/**
* Hack around core issue 6791934: sets the sorter changed flag if appropriate.
* Called after processing the event.
* @param e the sorter event received from the sorter
*/
protected void postprocessSorterChanged(RowSorterEvent e) {
filteredRowCountChanged = false;
if (forceRevalidate && e.getType() == RowSorterEvent.Type.SORTED) {
filteredRowCountChanged = e.getPreviousRowCount() != getRowCount();
}
}
//----> end hack around core issue 6791934:
因此,这是使用 SwingX
的另一个原因(如果缺少某些原因)。
关于java - 行过滤器在单元格更新事件中无法按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26408440/
我通过 spring ioc 编写了一些 Rest 应用程序。但我无法解决这个问题。这是我的异常(exception): org.springframework.beans.factory.BeanC
我对 TestNG、Spring 框架等完全陌生,我正在尝试使用注释 @Value通过 @Configuration 访问配置文件注释。 我在这里想要实现的目标是让控制台从配置文件中写出“hi”,通过
为此工作了几个小时。我完全被难住了。 这是 CS113 的实验室。 如果用户在程序(二进制计算器)结束时选择继续,我们需要使用 goto 语句来到达程序的顶部。 但是,我们还需要释放所有分配的内存。
我正在尝试使用 ffmpeg 库构建一个小的 C 程序。但是我什至无法使用 avformat_open_input() 打开音频文件设置检查错误代码的函数后,我得到以下输出: Error code:
使用 Spring Initializer 创建一个简单的 Spring boot。我只在可用选项下选择 DevTools。 创建项目后,无需对其进行任何更改,即可正常运行程序。 现在,当我尝试在项目
所以我只是在 Mac OS X 中通过 brew 安装了 qt。但是它无法链接它。当我尝试运行 brew link qt 或 brew link --overwrite qt 我得到以下信息: ton
我在提交和 pull 时遇到了问题:在提交的 IDE 中,我看到: warning not all local changes may be shown due to an error: unable
我跑 man gcc | grep "-L" 我明白了 Usage: grep [OPTION]... PATTERN [FILE]... Try `grep --help' for more inf
我有一段代码,旨在接收任何 URL 并将其从网络上撕下来。到目前为止,它运行良好,直到有人给了它这个 URL: http://www.aspensurgical.com/static/images/a
在过去的 5 个小时里,我一直在尝试在我的服务器上设置 WireGuard,但在完成所有设置后,我无法 ping IP 或解析域。 下面是服务器配置 [Interface] Address = 10.
我正在尝试在 GitLab 中 fork 我的一个私有(private)项目,但是当我按下 fork 按钮时,我会收到以下信息: No available namespaces to fork the
我这里遇到了一些问题。我是 node.js 和 Rest API 的新手,但我正在尝试自学。我制作了 REST API,使用 MongoDB 与我的数据库进行通信,我使用 Postman 来测试我的路
下面的代码在控制台中给出以下消息: Uncaught DOMException: Failed to execute 'appendChild' on 'Node': The new child el
我正在尝试调用一个新端点来显示数据,我意识到在上一组有效的数据中,它在数据周围用一对额外的“[]”括号进行控制台,我认为这就是问题是,而新端点不会以我使用数据的方式产生它! 这是 NgFor 失败的原
我正在尝试将我的 Symfony2 应用程序部署到我的 Azure Web 应用程序,但遇到了一些麻烦。 推送到远程时,我在终端中收到以下消息 remote: Updating branch 'mas
Minikube已启动并正在运行,没有任何错误,但是我无法 curl IP。我在这里遵循:https://docs.traefik.io/user-guide/kubernetes/,似乎没有提到关闭
每当我尝试docker组成任何项目时,都会出现以下错误。 我尝试过有和没有sudo 我在这台机器上只有这个问题。我可以在Mac和Amazon WorkSpace上运行相同的容器。 (myslabs)
我正在尝试 pip install stanza 并收到此消息: ERROR: No matching distribution found for torch>=1.3.0 (from stanza
DNS 解析看起来不错,但我无法 ping 我的服务。可能是什么原因? 来自集群中的另一个 Pod: $ ping backend PING backend.default.svc.cluster.l
我正在使用Hibernate 4 + Spring MVC 4当我开始 Apache Tomcat Server 8我收到此错误: Error creating bean with name 'wel
我是一名优秀的程序员,十分优秀!