更新复杂 JTable、TableModel 等的正确方法

2024-01-20

我的 GUI 显示了我的车辆park,以及我想要设置的车辆可用的在两个不同的车辆表(扩展 JTable 的类)。 对于可用的情况,我希望可以从agent(第三方软件)。 这两个表都显示了行中车辆的描述...为此我创建了车辆表模型 and Vehicle类。 Vehicle类是一个抽象类,他的子类有:Car、Truck、Trailer等。

You can see a snapshot of my software: enter image description here

我的问题是: 在我目前的实施中,我不认为管理真的很好updates行数


整个事情开始于TableModel实现,让我们看一下:

public class VehicleTableModel extends AbstractTableModel {

    private ArrayList<Vehicle> vehicles;

    // Most of your code here, didn't examine it closer though

    public void addRow(Vehicle vehicle) {
        int rowIndex = vehicles.size();
        vehicles.add(vehicle);
        fireTableRowsInserted(rowIndex, rowIndex); // just notify last row == vehicles.size() == getRowCount()
    }

    public void removeRow(int row) {
        vehicles.remove(row);
        fireTableRowsDeleted(row, row); // is right? yes, it looks ok.
    }

    @Override
    public void setValueAt(Object value, int row, int col) {
        Vehicle v = vehicles.get(row);
        if (v != null) {
            COLUMNS column = header[col];
            switch (column) {
                case TARGA_COLUMN:...; break;
                case MARCA_COLUMN:...; break;
                // others...
            }
            fireTableCellUpdated(row, column); // this is the appropriate fire method.
        }
    }

    /**
     * Convenience method to notify if a vehicle was updated in 
     * the outside, not through setValueAt(...).
     */
    public void notifyVehicleUpdated(Vehicle vehicle) {
        Vehicle[] elements = (Vehicles[])vehicles.toArray();
        for (int i = 0; i < elements.length; i++) {
            if (elements[i] == vehicle) {
                fireTableRowsUpdated(i, i);
            }
        }
    }

}

其他一些提示:

  • Never use repaint() nor updateUI()刷新表的数据。表模型有责任通知视图有关正确的事件。

  • Never use fireTableDataChanged()(正如有人建议的那样)除非whole表模型数据已更改。有合适的fireXxxx()行、列和单元格的方法发生变化。

  • 据我了解这个问题,两个表共享车辆列表,因此你必须保持它们同步。如果是这样,我想知道为什么需要两种不同的桌子模型?如果唯一的原因是可用/停放状态(互斥),那么您可以在两个表中共享一个表模型,并根据车辆的状态应用不同的过滤器。在状态字段更新时,两个表都将收到通知,并且车辆将被转入从一张桌子到另一张桌子。

Update

前段时间在评论里这个答案 https://stackoverflow.com/a/25536638/1795530添加一个方法的想法,例如notifyRowUpdated() to Coordinator抽象类似乎适合解决两个表之间的同步问题。

但现在我认为最好的方法是与两个表共享相同的表模型,并根据车辆的状态过滤第二个表:如果可用(DISPONIBILE)则显示它,如果不可用则隐藏它。

这样,在行更新和行删除时,两个表都会收到通知并采取相应的操作。在单元格更新时,我们可以向模型添加一个 TableModelListener,该模型在第二个表上应用过滤器,显示可用车辆并隐藏不可用车辆。更不用说Coordinator抽象类将保持简单并保持其原始目的:通知第三方代理行更新/删除。

因此,请查看下面的代码示例(抱歉扩展)。一些注意事项:

  • 我已经效仿你的Vehicle与一个更简单的类。状态由可用的布尔属性定义。
  • DataObjectTableModel代码可在表格模型 /questions/tagged/tablemodel tag wiki https://stackoverflow.com/tags/tablemodel/info我已经使用此类来模拟您的表模型。
  • 因为我没有任何CoordinatorI 类直接在表模型上添加/删除行,但您应该通过适当的协调器来执行此操作。
  • 不知道为什么我们必须对表格单元格更新事件重新应用过滤器。据我了解,应该通知表行排序器并自动应用过滤器。然而它并不能以这种方式工作,我们必须手动重新应用 Fitlers。不过是小问题。

代码示例

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.Arrays;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultRowSorter;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableColumnModel;

public class DemoSharedTableModel {

    private DataObjectTableModel<Vehicle> model;
    private JTable table1, table2;
    private Action addAction, removeAction;

    private void createAndShowGui() {

        String[] columnIdentifiers = new String[] {
            "Plates",
            "Description",
            "Available"
        };

       model = new DataObjectTableModel<Vehicle>(Arrays.asList(columnIdentifiers)) {

            @Override
            public Class<?> getColumnClass(int columnIndex) {
                switch (columnIndex) {
                    case 0:
                    case 1: return String.class;
                    case 2: return Boolean.class;
                }
                return super.getColumnClass(columnIndex);
            }

            @Override
            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return columnIndex == 2;
            }

            @Override
            public Object getValueAt(int rowIndex, int columnIndex) {
                Vehicle vehicle = getDataObject(rowIndex);
                switch (columnIndex) {
                    case 0 : return vehicle.getPlates();
                    case 1: return vehicle.getDescription();
                    case 2: return vehicle.isAvailable();
                        default: throw new ArrayIndexOutOfBoundsException(columnIndex);
                }
            }

            @Override
            public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
                if (columnIndex == 2) {
                    Vehicle vehicle = getDataObject(rowIndex);
                    vehicle.setAvailable((Boolean)aValue);
                    fireTableCellUpdated(rowIndex, columnIndex);
                } else {
                    throw new UnsupportedOperationException("Unsupported for column " + columnIndex);
                }
            }
        };

        model.addRow(new Vehicle("AAA1", "Car - Peugeot", true));
        model.addRow(new Vehicle("AAA2", "Truck - Volvo", true));
        model.addRow(new Vehicle("AAA3", "Car - Ford", false));
        model.addRow(new Vehicle("AAA4", "Car - Mercedes-Benz", false));
        model.addRow(new Vehicle("AAA5", "Car - Ferrari", true));

        model.addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                if (e.getType() == TableModelEvent.UPDATE) {
                    DemoSharedTableModel.this.applyFilterOnSecondTable();
                }
            }
        });

        table1 = new JTable(model);
        table1.setAutoCreateRowSorter(true);
        table1.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        table2 = new JTable(model);
        table2.setAutoCreateRowSorter(true);
        table2.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        // Make third column not visible
        TableColumnModel columnModel = table2.getColumnModel();
        columnModel.removeColumn(columnModel.getColumn(2));

        applyFilterOnSecondTable();

        addAction = new AbstractAction("+") {
            @Override
            public void actionPerformed(ActionEvent e) {
                model.addRow(new Vehicle("new", "default text", true));
            }
        };

        removeAction = new AbstractAction("-") {
            @Override
            public void actionPerformed(ActionEvent e) {
                int viewIndex = table1.getSelectedRow();
                if (viewIndex != -1) {
                    int modelIndex = table1.convertRowIndexToModel(viewIndex);
                    model.deleteRow(modelIndex);
                }
                setEnabled(model.getRowCount() > 0);
            }
        };

        JPanel buttonsPanel = new JPanel();
        buttonsPanel.add(new JButton(addAction));
        buttonsPanel.add(new JButton(removeAction));

        JPanel content = new JPanel(new BorderLayout(8, 8));
        content.add(new JScrollPane(table1), BorderLayout.WEST);
        content.add(buttonsPanel, BorderLayout.CENTER);
        content.add(new JScrollPane(table2), BorderLayout.EAST);

        JFrame frame = new JFrame("Demo");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(content);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private void applyFilterOnSecondTable() {
        DefaultRowSorter sorter = (DefaultRowSorter)table2.getRowSorter();
        sorter.setRowFilter(new RowFilter() {
            @Override
            public boolean include(RowFilter.Entry entry) {
                Vehicle vehicle = model.getDataObject((Integer)entry.getIdentifier());
                return vehicle.isAvailable();
            }
        });
    }

    class Vehicle {

        private String plates, description;
        private Boolean available;

        public Vehicle(String plates, String description, Boolean available) {
            this.plates = plates;
            this.description = description;
            this.available = available;
        }

        public String getPlates() {
            return plates;
        }

        public void setPlates(String plates) {
            this.plates = plates;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public Boolean isAvailable() {
            return available;
        }

        public void setAvailable(Boolean available) {
            this.available = available;
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new DemoSharedTableModel().createAndShowGui();
            }
        });
    }
}

截屏

请注意,第二个表中仅显示可用车辆。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

更新复杂 JTable、TableModel 等的正确方法 的相关文章

随机推荐

  • 使用 wpf HierarchicalDataTemplate 接口的任何方式

    您好 我正在使用接口 IFooNode 它是树的一部分 我想使用 HierarchicalDataTemplate 在 TreeView 中显示这棵树 然而 由于接口的原因 这不起作用 我看到两种方法 但都不是我所说的 好 找出真正实现 I
  • Python - Pandas - 对特定子集的 dropna 调用期间出现关键错误

    我的目标 我希望删除特定列中具有 NaN 的行 我将允许 NaN 存在于某些列上 但不允许存在于其他列上 英文示例 如果一行中 detail age 的值为 NaN 我想删除该行 这是我的数据的视图 import pandas as pd
  • 页面回发中的 Javascript 事件

    是否有回发时触发的 JavaScript 事件 如果没有 如何在页面回发之后或之前立即运行客户端代码 我相信您正在寻找的是Sys WebForms PageRequestManager beginRequest 事件 http msdn m
  • PHP:最安全(可解密)的加密方法?

    在 PHP 中 可解密的 加密算法是最安全的一种吗 我的意思是MD5无法解密 对吗 我找到了带有 mcrypt 的完整工作类 然后再次用 base64 编码 它可以加密并解密回来 示例 mcrypt 加密 function encrypt
  • 如何避免 Jenkins 多分支管道作业自行触发

    我希望我的 Jenkins 多分支管道作业能够避免自行触发 该作业进行提交 因为它会增加版本文件并将其签入源代码管理 这会导致无限循环 在正常工作中我可以遵循这些说明 https liviutudor com 2015 12 09 jenk
  • Android实时数据库抛出错误:客户端已离线但实际上并没有

    该应用程序执行简单的注册 使用 FirebaseAuth FirebaseUI 和 Google Sign In 认证时成功地 我拿firebaseUser userId并使用它从实时数据库中获取用户配置文件 示例位置 users user
  • 使用 TFS 2010“构建”经典 ASP

    我使用 TFS 2010 进行源代码控制和持续集成 除其他外 我有一个我维护的经典 ASP 应用程序 我想将其合并到持续集成构建中 但是 我必须在构建定义中提供 要构建的项目 由于经典 ASP 没有与之关联的解决方案或项目 因此我仍然可以使
  • Struts2 ValueStack如何处理多个请求

    我明白ValueStack在 Struts2 中引入 Struts1 模型的另一项变化现在是一个新的ActionObject为每个请求实例化 所以我们可以定义实例变量而不用担心多线程问题 拦截器和 JSP 访问实例变量的方式ActionOb
  • 如何抑制 ggplot2 图中的垂直网格线?

    我正在构建一个条形图 其中条形足以作为水平 x 放置的指示 因此我想避免绘制多余的垂直网格线 我了解如何在 opts 中设置次要和主要网格线的样式 但我一生都无法弄清楚如何仅抑制垂直网格线 library ggplot2 data lt d
  • Xamarin.Android - 本机代码编译是否会使逆向工程变得更加困难?

    我们正在考虑将 C NET 应用程序移植到 Android 我已经开始阅读有关 Xamarin Android 和 Mono 框架的内容 我刚刚开始Android 开发 我注意到主要http xamarin com android http
  • 在 Scikit-learn 中使用 Smote 和 Gridsearchcv

    我正在处理不平衡的数据集 并希望使用 scikit 的 gridsearchcv 进行网格搜索来调整模型的参数 为了对数据进行过采样 我想使用 SMOTE 并且我知道我可以将其作为管道的一个阶段并将其传递给 gridsearchcv 我担心
  • 当“下拉菜单值更改”时重新加载 d3 图表

    当用户在下拉菜单中选择一个项目以及与该项目对应的数据时 我试图重新加载 d3 js 折线图 我的菜单是股票市场价值列表 YHOO FB 对于其中每一个 我都有一个包含数据的 JSON 文件 该图本身正在发挥作用 我将代码放在 JSFiddl
  • 在 Active Admin 中禁用 CSV 下载

    我正在使用 Active Admin gem 我想隐藏或删除每个模型索引页面上的链接 允许用户下载 CSV XML 或 JSON 格式的数据 有什么办法可以做到这一点吗 现在有一个选项 download links在索引方法上 因此您可以根
  • 测试用例中缺少 ROLLBACK 会导致多数据库 django 应用程序中唯一约束冲突

    我刚刚开始使用工厂男孩 https github com dnerdy factory boy用于测试工厂的 django 库 并且存在重复键约束违规问题 测试成员程序 py from datetime import date timede
  • 如何从 aws 实例内部检测其状态?

    我在 EC2 中有自动缩放组 我想在实例终止时检测实例的状态 以便我可以在实例终止之前开始导出日志文件 我知道实现此目的的一种方法是使用自动缩放生命周期挂钩 但根据我的理解 我必须使用外部监视器 然后该监视器必须 ssh 到实例并导出日志文
  • 从 select 语句 mysql 调用用户定义的存储过程

    我试图从 select 语句调用用户定义的存储过程 但它给了我一个错误 但是 当我调用系统过程时 它工作得很好 有没有办法从 select 语句调用用户定义的过程 这是针对mysql的 SELECT ID email FROM user P
  • 在nodejs日期对象中设置日期将日期显示为不同的值

    当我使用下面的语句设置日期对象时 输出在 NodeJS 中显示不同 你能帮我理解为什么会这样吗 以及我需要如何传递来打印正确的值 var date1 new Date 2017 01 01 var date2 new Date 2017 0
  • Charts.js 直线 - 我找不到解决方案

    所以我使用charts jshttp www chartjs org http www chartjs org 我试图使两个点之间的线是直的而不是弯曲的 没有明显的原因 现在看起来像那样https i stack imgur com rK8
  • 我是否应该使用“self”来定义我不需要从外部访问的实例化类的变量/对象?

    我不是一个完全的初学者 但相当陌生Python 今天在做一个项目时 我只是有一个想法 并想知道 self 我过去读过一段时间 但我仍然无法弄清楚它是否总是必要的 我的问题仅涉及类的实例和实例参数 变量 这个问题与影响所有实例的类变量无关 E
  • 更新复杂 JTable、TableModel 等的正确方法

    我的 GUI 显示了我的车辆park 以及我想要设置的车辆可用的在两个不同的车辆表 扩展 JTable 的类 对于可用的情况 我希望可以从agent 第三方软件 这两个表都显示了行中车辆的描述 为此我创建了车辆表模型 and Vehicle