Add column to exiting TableModel
Asked Answered
G

6

5

I have a class;

public class A extends AbstractTableModel
{
 ...
}

Using ResultSetMetaData I build the TableModel to match my result set from the database.

public class B extends JPanel
{
 ...
}

In class B where I extends JPanel and added class A to show my table. I want to be able to add new columns base on a condition to the table model. I have tried googling around but most example shown are based on the DefaultTableModel not AbstractTableModel.

Do anyone know how to achieve this?

Glaucous answered 22/7, 2011 at 16:43 Comment(0)
K
3

Just extend DefaultTableModel and then you have access to all of its methods. DefaultTableModel also extends AbstractTableModel, so references to AbstractTableModel in other parts of your code aren't going to break.

Knotts answered 22/7, 2011 at 17:31 Comment(0)
P
16

for example

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;

public class TableColumnHider {

    private JTable table;
    private TableColumnModel tcm;
    private Map hiddenColumns;

    public TableColumnHider(JTable table) {
        this.table = table;
        tcm = table.getColumnModel();
        hiddenColumns = new HashMap();
    }

    public void hide(String columnName) {
        int index = tcm.getColumnIndex(columnName);
        TableColumn column = tcm.getColumn(index);
        hiddenColumns.put(columnName, column);
        hiddenColumns.put(":" + columnName, new Integer(index));
        tcm.removeColumn(column);
    }

    public void show(String columnName) {
        Object o = hiddenColumns.remove(columnName);
        if (o == null) {
            return;
        }
        tcm.addColumn((TableColumn) o);
        o = hiddenColumns.remove(":" + columnName);
        if (o == null) {
            return;
        }
        int column = ((Integer) o).intValue();
        int lastColumn = tcm.getColumnCount() - 1;
        if (column < lastColumn) {
            tcm.moveColumn(lastColumn, column);
        }
    }

    public static void main(String[] args) {
        String[] columnNames = {"Name", "Size", "Type", "Date Modified", "Permissions"};
        String[][] data = {
            {"bin", "2", "dir", "Jun 9", "drwxr-xr-x"},
            {"boot", "3", "dir", "Jun 9", "drwxr-xr-x"},
            {"dev", "6", "dir", "Jul 12", "drwxr-xr-x"},
            {"etc", "34", "dir", "Jul 12", "drwxr-xr-x"},};
        JTable table = new JTable(data, columnNames);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        JScrollPane scrollPane = new JScrollPane(table);
        final TableColumnHider hider = new TableColumnHider(table);
        JPanel checkBoxes = new JPanel();
        for (int i = 0; i < columnNames.length; i++) {
            JCheckBox checkBox = new JCheckBox(columnNames[i]);
            checkBox.setSelected(true);
            checkBox.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent evt) {
                    JCheckBox cb = (JCheckBox) evt.getSource();
                    String columnName = cb.getText();

                    if (cb.isSelected()) {
                        hider.show(columnName);
                    } else {
                        hider.hide(columnName);
                    }
                }
            });
            checkBoxes.add(checkBox);
        }
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(scrollPane);
        frame.getContentPane().add(checkBoxes, BorderLayout.SOUTH);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
Parodist answered 22/7, 2011 at 22:16 Comment(1)
+1 I couldn't resist posing this generic variation of your delightful example.Hornbill
H
11

Just for fun, here's a generic version of @mKorbel's TableColumnHider.

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;

/** @see https://stackoverflow.com/questions/6796673 */
public class TableColumnHider {

    private JTable table;
    private TableColumnModel tcm;
    private Map<String, IndexedColumn> hidden =
        new HashMap<String, IndexedColumn>();

    public TableColumnHider(JTable table) {
        this.table = table;
        this.tcm = table.getColumnModel();
    }

    public void hide(String columnName) {
        int index = tcm.getColumnIndex(columnName);
        TableColumn column = tcm.getColumn(index);
        IndexedColumn ic = new IndexedColumn(index, column);
        if (hidden.put(columnName, ic) != null) {
            throw new IllegalArgumentException("Duplicate column name.");
        }
        tcm.removeColumn(column);
    }

    public void show(String columnName) {
        IndexedColumn ic = hidden.remove(columnName);
        if (ic != null) {
            tcm.addColumn(ic.column);
            int lastColumn = tcm.getColumnCount() - 1;
            if (ic.index < lastColumn) {
                tcm.moveColumn(lastColumn, ic.index);
            }
        }
    }

    private static class IndexedColumn {

        private Integer index;
        private TableColumn column;

        public IndexedColumn(Integer index, TableColumn column) {
            this.index = index;
            this.column = column;
        }
    }

    public static void main(String[] args) {
        String[] columnNames = {
            "Name", "Size", "Type", "Date Modified", "Permissions"
        };
        String[][] data = {
            {"bin", "2", "dir", "Jun 9", "drwxr-xr-x"},
            {"boot", "3", "dir", "Jun 9", "drwxr-xr-x"},
            {"dev", "6", "dir", "Jul 12", "drwxr-xr-x"},
            {"etc", "34", "dir", "Jul 12", "drwxr-xr-x"}
        };
        JTable table = new JTable(data, columnNames);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        JScrollPane scrollPane = new JScrollPane(table);
        final TableColumnHider hider = new TableColumnHider(table);
        JPanel checkBoxes = new JPanel();
        for (int i = 0; i < columnNames.length; i++) {
            JCheckBox checkBox = new JCheckBox(columnNames[i]);
            checkBox.setSelected(true);
            checkBox.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent evt) {
                    JCheckBox cb = (JCheckBox) evt.getSource();
                    String columnName = cb.getText();

                    if (cb.isSelected()) {
                        hider.show(columnName);
                    } else {
                        hider.hide(columnName);
                    }
                }
            });
            checkBoxes.add(checkBox);
        }
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(scrollPane);
        frame.getContentPane().add(checkBoxes, BorderLayout.SOUTH);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
Hornbill answered 23/7, 2011 at 3:17 Comment(1)
that's correctt, because I missed here any indication for WidlCard +1Parodist
K
3

Just extend DefaultTableModel and then you have access to all of its methods. DefaultTableModel also extends AbstractTableModel, so references to AbstractTableModel in other parts of your code aren't going to break.

Knotts answered 22/7, 2011 at 17:31 Comment(0)
H
2

Surely DefaultTableModel is more convenient, but AbstractTableModel offers more flexibility and less overhead. There's an example here that shows a model containing a List<Value>, where Value is a POJO. More examples may be found here.

Hornbill answered 22/7, 2011 at 18:5 Comment(1)
See also this example using AbstractTableModel.Hornbill
C
1

Beware that the code examples from mKorbel and trashgod doesn't preserve the original order of the columns. Just try hiding all the columns in sequence and then showing them in the same sequence again (Name->Size->Type->Date Modified->Permissions), and you'll see that the original order is lost.

If you need to preserve the original order of the columns when showing them again, look at Stephen Kelvin's solution.

Unfortunately, the confusion between "view" and "model" is prevalent in JTable API. Even the method names are misleading. For some enlightenment, see a related discussion here.

Cutlery answered 17/1, 2012 at 14:3 Comment(2)
the confusion between "view" and "model" is prevalent in JTable API no, that's not true: on the level of JTable, everything is in view coordinatesSpoiler
@kleopatra, I meant the difficulty to distinguish between model and view concerning the JTable and aggregated classes, e.g.: table.getColumnModel().getColumnCount(); // "2" versus table.getModel().getColumnCount(); // "3", like demonstrated here.Cutlery
W
0

I have adapted DefaultTableModel's addColumn method to a custom AbstractTableModel as follows. Assume that both the column identifiers (headers) and model data (localCache) are ArrayLists - the model data being an ArrayList of an ArrayList.

public void addColumn(String columnName, List columnData) {
    headers.add(columnName);
    colCount = headers.size();
    if (columnData != null) {
        for (int r = 0; r < localCache.size(); r++) {
            ((List)localCache.get(r)).add(columnData.get(r));
        }
    } else {
        System.out.println("Null columnData passed");
    }
    fireTableStructureChanged();
} 
Wendt answered 12/4, 2012 at 17:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.