Complex use of JTable, TableModel and others

好久不见. 提交于 2019-12-01 14:31:45

"I want know a way for use a Icon in jTable but DON'T want handle the icon like a part of the informations. I want that the jTable realizes by itself the kind of vehicle and change the icon of conseguency"

You're going to have a really tough time trying to do it with this:

Object[] vehicle = {"aaa", "kind1", "marca", "disponibile", "ptt" };

Better to use the other one, with the value for the image columns. Remember, you can have null values.

First things first, this:

Object[] vehicle = {"/image/truck.png" ,"aaa", "kind1",
                    "marca", "disponibile", "ptt" };

Can be this

Object[] vehicle = {new ImageIcon(...) ,"aaa", "kind1", 
                    "marca", "disponibile", "ptt" }

Since, you've overridden getColumnClass(), the default renderer will render the column as an image, as ImageIcon.class will be detected. See How to use Tables: Editors and Renderers.

As for the main question, how you can change the image dynamically based on the kind of vehicle change, you can override setValueAt of the table model. Something like

DefaultTableModel model = new DefaultTableModel(...) {
    private static final int CAR_TYPE_COLUMN = 2;
    private static final int IMAGE_COLUMN = 0;

    @Override
    public void setValueAt(Object value, int row, int col) {
        if (col == CAR_TYPE_COLUMN) {
            ImageIcon icon = findImageByColumnCarType(value);
            super.setValueAt(icon, row, IMAGE_COLUMN);
        }
        super.setValueAt(value, row, col);
    }

    public Class<?> getColumnClass(int columnIndex) {
        return getValueAt(0, columnIndex).getClass();
    }
};

Where findImageByColumnCarType is some method to find the ImageIcon based on the value. setValueAt will be called by the editor, in your case the combobox. So when the value is being set, the value of combo box will be passed to the setValueAt, and you can use that to call the method findImageByColumnCarType to get the ImageIcon. You could have a Map or something that you use to hold the icons and corresponding values. You can have the method return null for a car type that has no image

Once you have the ImageIcon you want, it's just a matter of calling super.setValueAt to set the new icon for the image column for that same row.

I have understand that my first implementation isn't good for my exigence. First things first, the using of DefaultTableModel is a great limitation, because you can use only Vector, Object, String... In my case, I want manipulate by jtable a set of Vehicles. So, the first things:

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);
    }

    // ...

}

You can see the results here:

I still need to define a couple of things, but it should be the right way.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!