我在管理两个JTable和相关数据时遇到一些问题。
我做了这个GUI: 在此处输入图片说明
我解释一下用法:在第一个jTable中,我列出了车辆(卡车, 汽车……)和相关信息。在第二个jTable中 ,我按下 绿色箭头,列出了我要使其“可用”的车辆(即sw代理启动)。因此,是第一个列表的子列表:在第一个 表中选择的行将在第二个表中复制。
第一个问题:在第一列中,我有 车辆类型的说明图(在示例中您可以看到卡车)。在第三列和第五列中,我 有不同的jComboBoxs。请参阅TIPO VEICOLO(即Kind)的jComboBox:如果 选择其他车辆,则第一列中的图像必须更改!(如果我 选择从卡车更改为汽车,则相对图标必须更改)。而且, 可能的话,我不想在软件的其他地方处理此图标。我 解释:我想用
Object[] vehicle = {"aaa", "kind1", "marca", "disponibile", "ptt" }
and never
Object[] vehicle = {"/image/truck.png" ,"aaa", "kind1", "marca", "disponibile", "ptt" }
第二个问题:将来,我想在jTable中添加其他功能: 尺寸,车辆的颜色,许可证……我知道卡车 的功能与汽车的功能不同。我想要一种将所有功能保留在 标题中的方法,但要在每行中(根据车辆的类型) 激活/禁用某些单元格。
请注意:jTables的标题不一定相同
package it.transfersimulation; import it.transfersimulation.Vehicle.Stato; import it.transfersimulation.Vehicle.TipoVeicolo; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.ComponentOrientation; import javax.swing.DefaultCellEditor; import javax.swing.DefaultComboBoxModel; import javax.swing.ImageIcon; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.BoxLayout; import javax.swing.JTable; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import javax.swing.JButton; import java.awt.FlowLayout; import javax.swing.SwingConstants; @SuppressWarnings("serial") public class ShipperAgentGUI extends JFrame implements ActionListener { // Variabili di classe private JPanel masterPanel; private JButton btnPM_plus; private JButton btnPM_meno; private JButton btnMD_plus; private JButton btnMD_meno; private JTable availablesTable; private JTable parkTable; private Object[] parkModelHeader = { "" , "TARGA", "TIPO VEICOLO", "MARCA", "STATO", "PTT" }; private Object[] availablesModelHeader = { "", "TARGA", "TIPO VEICOLO", "MARCA", "STATO", "PTT" }; private DefaultTableModel parkModel = new DefaultTableModel(null, parkModelHeader){ public Class<?> getColumnClass(int columnIndex) { return getValueAt(0, columnIndex).getClass(); }; };// per aggiungere jCheckBox, jComboBox e ImageIcon private DefaultTableModel availablesModel = new DefaultTableModel(null, availablesModelHeader){ public Class<?> getColumnClass(int columnIndex) { return getValueAt(0, columnIndex).getClass(); }; };// per aggiungere jCheckBox, jComboBox e ImageIcon // My third-part software: a JADE agent: protected ShipperAgent shipperAgent; private Coordinator parkCoordinator; private Coordinator availablesCoordinator; //////////////////////////////////////////////////// // COSTRUTTORE ShipperAgentGUI(ShipperAgent agent) { // Valorizza l'agente corrispondente shipperAgent = agent; /////////////////////////////////////////////////////////////////////// // Graphics: // setTitle("Shipper Agent: "+agent.getLocalName()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); try { UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel"); //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { e.printStackTrace(); } // MasterPanel masterPanel = new JPanel(); masterPanel.setLayout(new BoxLayout(masterPanel, BoxLayout.Y_AXIS)); masterPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); // Park Panel JPanel parkPanel = new JPanel(); parkPanel.setLayout(new BoxLayout(parkPanel, BoxLayout.Y_AXIS)); masterPanel.add(parkPanel); JPanel pnlHeaderParkPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); JLabel parkLabel = new JLabel("Parco auto:"); pnlHeaderParkPanel.add(parkLabel); parkPanel.add(pnlHeaderParkPanel); JPanel pnlTableParkPanel = new JPanel(); pnlTableParkPanel.setLayout(new BoxLayout(pnlTableParkPanel, BoxLayout.X_AXIS)); parkPanel.add(pnlTableParkPanel); // Park Table parkTable = new JTable(); parkTable.setModel(parkModel); parkTable.setPreferredScrollableViewportSize(new Dimension(500,100)); parkTable.setFillsViewportHeight(true); JScrollPane parkScrollPane = new JScrollPane(parkTable); pnlTableParkPanel.add(parkScrollPane); JPanel pnlBtnParkPanel = new JPanel(); pnlTableParkPanel.add(pnlBtnParkPanel); pnlBtnParkPanel.setLayout(new BoxLayout(pnlBtnParkPanel, BoxLayout.Y_AXIS)); // JButtons: add/remove vehicle in Park Table btnPM_plus = new JButton(); btnPM_plus.setToolTipText("Aggiungi mezzo"); btnPM_plus.setIcon(new ImageIcon(ShipperAgentGUI.class.getResource("/images/lorry-add.png"))); btnPM_plus.setActionCommand("+parco"); btnPM_plus.addActionListener(this); pnlBtnParkPanel.add(btnPM_plus); btnPM_meno = new JButton(); btnPM_meno.setToolTipText("Rimuovi mezzo"); btnPM_meno.setIcon(new ImageIcon(ShipperAgentGUI.class.getResource("/images/lorry-delete.png"))); btnPM_meno.setActionCommand("-parco"); btnPM_meno.addActionListener(this); pnlBtnParkPanel.add(btnPM_meno); // Arrow Panel JPanel arrowPanel = new JPanel(); masterPanel.add(arrowPanel); // JButtons: available or not vehicle btnMD_plus = new JButton(); btnMD_plus.setToolTipText("Rendi disponibile il mezzo selezionato"); btnMD_plus.setIcon(new ImageIcon(ShipperAgentGUI.class.getResource("/images/arrow-green-down.png"))); arrowPanel.add(btnMD_plus); btnMD_plus.setActionCommand("+disponibili"); btnMD_plus.addActionListener(this); btnMD_meno = new JButton(); btnMD_meno.setToolTipText("Rendi indisponibile il mezzo selezionato"); btnMD_meno.setIcon(new ImageIcon(ShipperAgentGUI.class.getResource("/images/arrow-red-up.png"))); arrowPanel.add(btnMD_meno); btnMD_meno.setActionCommand("-disponibili"); btnMD_meno.addActionListener(this); // Availables Panel JPanel availablesPanel = new JPanel(); masterPanel.add(availablesPanel); availablesPanel.setLayout(new BoxLayout(availablesPanel, BoxLayout.Y_AXIS)); JPanel pnlHeaderAvailablesPanel = new JPanel(); FlowLayout fl_pnlHeaderAvailablesPanel = (FlowLayout) pnlHeaderAvailablesPanel.getLayout(); fl_pnlHeaderAvailablesPanel.setAlignment(FlowLayout.LEFT); availablesPanel.add(pnlHeaderAvailablesPanel); JLabel label_1 = new JLabel("Disponibili:"); pnlHeaderAvailablesPanel.add(label_1); label_1.setHorizontalAlignment(SwingConstants.LEFT); // Available Table availablesTable = new JTable(); availablesTable.setModel(availablesModel); availablesTable.setPreferredScrollableViewportSize(new Dimension(500, 100)); availablesTable.setFillsViewportHeight(true); JScrollPane availablesScrollPane = new JScrollPane(availablesTable); availablesPanel.add(availablesScrollPane); getContentPane().add(masterPanel, BorderLayout.CENTER); // Search Panel JPanel searchPanel = new JPanel(); masterPanel.add(searchPanel); JButton btnSearch = new JButton("Search"); searchPanel.add(btnSearch); // End of graphics init /////////////////////////////////// ////////////////////////////////////// // Editor delle colonne delle tabelle // TODO JComboBox<TipoVeicolo> tipoVeicoloComboBox = new JComboBox<TipoVeicolo>(); tipoVeicoloComboBox.setModel(new DefaultComboBoxModel<TipoVeicolo>(TipoVeicolo.values())); JComboBox<Stato> statoComboBox = new JComboBox<Stato>(); statoComboBox.setModel(new DefaultComboBoxModel<Stato>(Stato.values())); TableColumn tipoVeicoloColumn = parkTable.getColumnModel().getColumn(2); TableColumn statoColumn = parkTable.getColumnModel().getColumn(4); tipoVeicoloColumn.setCellEditor(new DefaultCellEditor(tipoVeicoloComboBox)); statoColumn.setCellEditor(new DefaultCellEditor(statoComboBox)); ///////////////////////////////////////////////////////////////////// // Coordinators (ispirati al Mediator pattern) parkCoordinator = new Coordinator(shipperAgent, parkModel) { @Override public void notifyAndAddRow(final Object[] rowData) { shipperAgent.newTruck((String) rowData[0]); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { tableModel.addRow(rowData); } }); } @Override public void notifyAndDeleteRow(final int rowIndex) { final String truck = (String)this.tableModel.getValueAt(rowIndex, 0); int flag=search(availablesCoordinator.tableModel, truck); if (flag!=-1) removeVehicle(availablesCoordinator, flag); shipperAgent.removeTruck(truck); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { tableModel.removeRow(rowIndex); } }); } }; availablesCoordinator = new Coordinator(shipperAgent, availablesModel) { @Override public void notifyAndAddRow(final Object[] rowData) { shipperAgent.activateTruck((String) rowData[0]); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { tableModel.addRow(rowData); } }); } @Override public void notifyAndDeleteRow(final int rowIndex) { String truck = (String)this.tableModel.getValueAt(rowIndex, 1); shipperAgent.deactivateTruck(truck); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { tableModel.removeRow(rowIndex); } }); } }; ///////////////////////////////////////////////////// // Listeners: parkModel.addTableModelListener(parcoListener); availablesModel.addTableModelListener(mezziDisponibiliListener); ///////////////////////////////////////////////////// // Contatto con l'agente - Riempimento dati // TODO Object[] veicoli = shipperAgent.getVehicles(); for (int i=0; i<veicoli.length;i++){ Object[] info = (Object[]) veicoli[i]; Object[] veicolo = new Object[info.length+1]; veicolo[0] = new ImageIcon(ShipperAgentGUI.class.getResource("/images/lorry-icon.png")); for (int j=1;j<info.length+1;j++){ veicolo[j]=info[j-1]; } parkModel.addRow(veicolo); if ( veicolo[4] == Stato.DISPONIBILE ) availablesModel.addRow(veicolo); } //////////////////////////// // Show GUI showGui(); } public void showGui() { pack(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int centerX = (int) screenSize.getWidth() / 2; int centerY = (int) screenSize.getHeight() / 2; setLocation(centerX - getWidth() / 2, centerY - getHeight() / 2); super.setVisible(true); } ////////////////////////////////////////////// // actionPerformed @Override public void actionPerformed(ActionEvent e) { switch (e.getActionCommand()) { case "+parco": { new VehicleInsertJDialog(this, parkCoordinator); } break; case "-parco": { int selectedRow = parkTable.getSelectedRow(); if (selectedRow != -1) removeVehicle(parkCoordinator, selectedRow); } break; case "+disponibili": { int selectedRow = parkTable.getSelectedRow(); if (selectedRow != -1){ //TODO controlla la consistenza addVehicle(availablesCoordinator, String.valueOf(parkModel.getValueAt(selectedRow, 0)), String.valueOf(parkModel.getValueAt(selectedRow, 1)), String.valueOf(parkModel.getValueAt(selectedRow, 2)), String.valueOf(parkModel.getValueAt(selectedRow, 3)), String.valueOf(parkModel.getValueAt(selectedRow, 4)) ); } } break; case "-disponibili": { int selectedRow = availablesTable.getSelectedRow(); if (selectedRow != -1) removeVehicle(availablesCoordinator, selectedRow); } break; default: System.out.println("Imprevisto in actionPerformed()"); break; } } // ///////////////////////////////////// // Add/Remove vehicles methods public void addVehicle(Coordinator coordinator, String targa, String tipo, String marca, String stato, String peso) { coordinator.notifyAndAddRow(new Object[]{targa, tipo, marca, stato, peso}); } public void removeVehicle(Coordinator coordinator, int index) { coordinator.notifyAndDeleteRow(index); } // ////////////////////////////////////////// // LISTENER: TableModelListener parcoListener = new TableModelListener() { public void tableChanged(TableModelEvent e) { switch (e.getType()) { case (TableModelEvent.INSERT): System.out.println("un inserimento in corso!"); break; case (TableModelEvent.DELETE): System.out.println("una cancellazione in corso!"); break; case (TableModelEvent.UPDATE): System.out.println("un aggiornamento in corso!"); break; } } }; TableModelListener mezziDisponibiliListener = new TableModelListener() { public void tableChanged(TableModelEvent e) { switch (e.getType()) { case (TableModelEvent.INSERT): System.out.println("un inserimento in corso!"); break; case (TableModelEvent.DELETE): System.out.println("una cancellazione in corso!"); break; case (TableModelEvent.UPDATE): System.out.println("un aggiornamento in corso!"); break; } } }; private int search(DefaultTableModel tableModel, String targa) { int flag = -1; for (int i=0; i<tableModel.getRowCount(); i++) if (tableModel.getValueAt(i, 0).equals(targa)) flag=i; return flag; } /////////////////////////////////////// // INNER CLASS /////////////////////////////////////// protected abstract class Coordinator { /* * protected class members so subclasses can access these directly */ protected ShipperAgent shipperAgent; protected DefaultTableModel tableModel; public Coordinator(ShipperAgent sa, DefaultTableModel tm) { shipperAgent = sa; tableModel = tm; } public abstract void notifyAndAddRow(Object[] rowData); public abstract void notifyAndDeleteRow(int rowIndex); } }
我知道我的第一个实现方式并不适合我的要求。首先,使用DefaultTableModel是一个很大的限制, 因为您只能使用Vector,Object,String …就我而言,我想通过jtable操纵一组Vehicles。因此,第一件事:
Vehicle.java
public class Vehicle { enum TipoVeicolo { AUTO, FURGONE, AUTOCARRO, AUTOARTICOLATO }; private String targa; private TipoVeicolo tipoVeicolo; private String marca; private Stato stato private float ptt; //... others... // constructor public Vehicle(String targa, TipoVeicolo tipoVeicolo, String marca, Stato stato, float ptt) { this.targa=targa; this.tipoVeicolo=tipoVeicolo; // ... bla bla } // GET and SET methods... //... }
Now, I had already done this. The new is the class that extends AbstractTableModel (and not DefaultTableModel)
VehicleTableModel.java
public class VehicleTableModel extends AbstractTableModel { // private objects private ArrayList<Vehicle> vehicles; private COLUMNS[] header; // possible column names: public enum COLUMNS { IMAGE_COLUMN, TARGA_COLUMN, CAR_TYPE_COLUMN, //... }; // if I want I can add others... /////////////////////////////////////////////////////// // Constructor: public VehicleTableModel(COLUMNS[] headerTable) { this.vehicles = new ArrayList<Vehicle>() this.header = headerTable; } /////////////////////////////////////////////////////// // obligatory override methods (from AbstractTableModel): @Override public int getColumnCount() { return header.length; } @Override public int getRowCount() { return vehicles.size(); } // this works! :D @Override public Object getValueAt(int row, int col) { Object value = "?"; Vehicle v = vehicles.get(row); if (v!=null) { COLUMNS column = header[col]; switch (column) { case IMAGE_COLUMN: int i = findColumn(COLUMNS.CAR_TYPE_COLUMN); // find the right column index Object tipo = getValueAt(row, i); value = (ImageIcon)findImageByColumnCarType(tipo); // find the right icon for the type of vehicle. break; case TARGA_COLUMN: value = v.getTarga(); break; case CAR_TYPE_COLUMN: value = v.getTipoVeicolo(); break; //... } } return value; } /////////////////////////////////////////////////////// // My methods: public void addRow(Vehicle vehicle) { if (!vehicles.contains(vehicle)){ vehicles.add(vehicle); fireTableRowsInserted(0, getRowCount()); // I'm not so sure of this.. } /* I'm not so sure of this.. public boolean removeRow(Vehicle vehicle) { boolean flag = vehicles.remove(vehicle); fireTableRowsDeleted(0, getRowCount()); // or fireTableDataChanged(); ? return flag; }*/ public void removeRow(int row) { vehicles.remove(row); fireTableRowsDeleted(row, row); } public Vehicle getVehicleAt(int row) { return vehicles.get(row); } // found the corresponding column index public int findColumn(COLUMNS columnName) { for (int i=0; i<getColumnCount(); i++) if (columnName.equals(header[i])) return i; return -1; } // found the right image protected static ImageIcon findImageByColumnCarType(Object value) { ImageIcon i = null; if (value.equals(TipoVeicolo.AUTO)) i = new ImageIcon(VehicleTableModel.class.getResource("/images/Car-icon_32.png")); else if (value.equals(TipoVeicolo.AUTOARTICOLATO)) i = new ImageIcon(VehicleTableModel.class.getResource("/images/City-Truck-blue-icon_32.png")); //... return i; } // knows if exist a value (of a column) in all rows private boolean controllIfExist(Object value, int col) { boolean bool = false; for (int i=0; i<getRowCount();i++){ if (value.equals(getValueAt(i, col))){ bool=true; break; } } return bool; } /////////////////////////////////////////////////////// // other methods (from AbstractTableModel) to ovveride: // this works! :D @Override public Class<?> getColumnClass(int col) { Class<?> c; COLUMNS column = header[col]; if (column.equals(COLUMNS.IMAGE_COLUMN)) c = ImageIcon.class; else if (column.equals(COLUMNS.CAR_TYPE_COLUMN)) c = JComboBox.class; // else if blabla.... else c = super.getColumnClass(col); return c; } // this works! :D @Override public String getColumnName(int col) { COLUMNS column = header[col]; if (column.equals(COLUMNS.IMAGE_COLUMN)) return " "; else if (column.equals(COLUMNS.TARGA_COLUMN)) return "Targa"; else if (column.equals(COLUMNS.CAR_TYPE_COLUMN)) return "Tipo veicolo"; // else if blabla... return super.getColumnName(col); }; @Override public boolean isCellEditable(int row, int col) { return true; } @Override public void setValueAt(Object value, int row, int col) { Vehicle v = vehicles.get(row); boolean flag = false; if (v!=null) { COLUMNS column = header[col]; switch (column) { case TARGA_COLUMN: if (!v.getTarga().equals(value)){ if (!controllIfExist(value, col)){ v.setTarga((String) value); flag = true; } } break; case CAR_TYPE_COLUMN: if (!v.getTipoVeicolo().equals(value)){ v.setTipoVeicolo((TipoVeicolo) value); flag = true; } break; // other cases bla bla... } if (flag) // update only if have found modify fireTableRowsUpdated(0, getRowCount()); // or fireTableRowsUpdated(row, row); ? } } }
After this, for commodity, I create a VehicleTable (extends JTable). Can it look like useless, but is right for my objective… You can see the right settings for the special cells (cell with JComboBox, for example)
VehicleTable.java
public class VehicleTable extends JTable { public VehicleTable(VehicleTableModel vehicleModel) { super(vehicleModel); this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); this.setColumnSelectionAllowed(false); this.setCellSelectionEnabled(false); this.setRowSelectionAllowed(true); this.setShowHorizontalLines(true); this.setRowHeight(25); this.setPreferredScrollableViewportSize(new Dimension(700,150)); this.setFillsViewportHeight(true); //////////////////////////////////// // Now I set the columns features: int flag=-1; TableColumn column; // Icon Column: flag = vehicleModel.findColumn(COLUMNS.IMAGE_COLUMN); if (flag!=-1){ column = this.getColumnModel().getColumn(flag); column.setMinWidth(80); column.setMaxWidth(80); } // Targa Column: flag = vehicleModel.findColumn(COLUMNS.TARGA_COLUMN); if (flag!=-1){ column = this.getColumnModel().getColumn(flag); column.setMinWidth(100); column.setMaxWidth(100); } // Tipo veicolo Column flag = vehicleModel.findColumn(COLUMNS.CAR_TYPE_COLUMN); if (flag!=-1){ column = this.getColumnModel().getColumn(flag); column.setCellEditor(new DefaultCellEditor( new JComboBox<TipoVeicolo>(TipoVeicolo.values()))); column.setMinWidth(150); column.setMaxWidth(150); } //others... }
Finally, we can use this. For example in my GUI
ShipperAgentGUI.java (an extract. I focus on ONE table)
public class ShipperAgentGUI extends JFrame implements ActionListener { // ... bla bla private COLUMNS[] parkModelHeader = {COLUMNS.IMAGE_COLUMN, COLUMNS.TARGA_COLUMN, COLUMNS.CAR_TYPE_COLUMN, COLUMNS.MARCA_COLUMN, COLUMNS.STATE_COLUMN, COLUMNS.PTT_COLUMN }; private VehicleTableModel parkModel = new VehicleTableModel(parkModelHeader); private VehicleTable parkTable; private Coordinator parkCoordinator; // long story protected ShipperAgent shipperAgent; // my agent, my third-part software // ... bla bla // Constructor: ShipperAgentGUI(ShipperAgent agent) { //... bla bla // Park Table: parkTable = new VehicleTable(parkModel); JScrollPane parkScrollPane = new JScrollPane(parkTable); pnlTableParkPanel.add(parkScrollPane); //... bla bla // Coordinators (Mediator pattern's ispired) // Long story. Is for coordinating with my agent and others tables in my GUI parkCoordinator = new Coordinator(shipperAgent, parkModel) { @Override public void notifyAndAddRow(final Vehicle vehicle) { shipperAgent.newTruck(vehicle.getTarga()); // comunicate with the agent SwingUtilities.invokeLater(new Runnable() { @Override public void run() { parkModel.addRow(vehicle); } }); } @Override public void notifyAndDeleteRow(final int rowIndex) { final Vehicle v = this.tableModel.getVehicleAt(rowIndex); // bla bla shipperAgent.removeTruck(v.getTarga()); // comunicate with the agent SwingUtilities.invokeLater(new Runnable() { @Override public void run() { //parkModel.removeRow(v); parkModel.removeRow(rowIndex); } }); } @Override public void notifyRowUpdated() { parkModel.addTableModelListener(new TableModelListener() { public void tableChanged(TableModelEvent e) { switch (e.getType()) { case (TableModelEvent.DELETE): parkTable.repaint(); break; case (TableModelEvent.UPDATE): int row = e.getLastRow(); int col = parkModel.getIndexColumn(COLUMNS.STATE_COLUMN); if (parkModel.getValueAt(row, col).equals(Stato.DISPONIBILE)) addVehicle(availablesCoordinator, parkModel.getVehicleAt(row)); else //removeVehicle(availablesCoordinator, row); error! availablesModel.removeRow(parkModel.getVehicleAt(row)); repaint(); break; } } }); } }; ArrayList<Vehicle> veicoli = shipperAgent.getVehicles(); // from agent Iterator<Vehicle> I = veicoli.iterator(); while (I.hasNext()){ addVehicle(parkCoordinator, I.next()); } //... bla bla } // end of constructor // ... others methods... private void addVehicle(Coordinator coordinator, Vehicle v) { coordinator.notifyAndAddRow(v); } public void removeVehicle(Coordinator coordinator, int index) { coordinator.notifyAndDeleteRow(index); } // ... }