Ramin
Ramin

Reputation: 484

synchronize view of two JTable

I have two JTables one in main viewport and one in footer viewport, using JideScrollPane. the problem is when the main JTable's view is customized, the footer JTable remains the same, is there any way to synchronize their view?

thanks.

unsynch

Upvotes: 4

Views: 1871

Answers (3)

René Link
René Link

Reputation: 51353

Usually this is done by using the same model for different ui components. Sadly the JTable contains a bug that will cause problems when sharing the TableColumnModel.

But you can work around it using this JTable

class ShareableColumnModelTable extends JTable {

    /**
     * Fixes http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4816146 and
     * more...
     * 
     */
    @Override
    public void columnMarginChanged(ChangeEvent e) {
        if (isEditing()) {
            removeEditor();
        }
        TableColumn resizingColumn = null;
        if (tableHeader != null) {
            resizingColumn = tableHeader.getResizingColumn();
        }
        if (resizingColumn != null) {
            if (autoResizeMode == AUTO_RESIZE_OFF) {
                resizingColumn.setPreferredWidth(resizingColumn.getWidth());
            } else { // this else block is missing in jdk1.4 as compared to
                        // 1.3
                TableColumnModel columnModel = getColumnModel();

                /**
                 * Temporarily disconnects this column listener to prevent
                 * stackoverflows if the column model is shared between
                 * multiple JTables.
                 */
                columnModel.removeColumnModelListener(this);
                try {
                    doLayout();
                } finally {
                    columnModel.addColumnModelListener(this);
                }

                repaint();
                return;
            }
        }
        resizeAndRepaint();
    }

}

With the ShareableColumnModelTableshowed above you can share one column model bettween multiple tables.

public static void main(String[] args) {
    JFrame frame = new JFrame("Column Sync");

    Container contentPane = frame.getContentPane();
    JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
    splitPane.setResizeWeight(0.5d);
    contentPane.add(splitPane);

    JTable table1 = new ShareableColumnModelTable();
    JTable table2 = new ShareableColumnModelTable();

    TableColumnModel tableColumnModel = createTableColumnModel();

    table1.setModel(createTableModel1());
    table2.setModel(createTableModel2());

    table1.setColumnModel(tableColumnModel);
    table2.setColumnModel(tableColumnModel);

    splitPane.setLeftComponent(new JScrollPane(table1));
    splitPane.setRightComponent(new JScrollPane(table2));

    showFrame(frame);
}

private static TableColumnModel createTableColumnModel() {
    TableColumnModel tableColumnModel = new DefaultTableColumnModel();

    TableColumn column1 = new TableColumn(0);
    column1.setHeaderValue("1. column");
    tableColumnModel.addColumn(column1);

    TableColumn column2 = new TableColumn(1);
    column2.setHeaderValue("2. column");
    tableColumnModel.addColumn(column2);

    return tableColumnModel;
}

private static TableModel createTableModel1() {
    DefaultTableModel tableModel = new DefaultTableModel();
    tableModel.setColumnCount(2);
    tableModel.addRow(new Object[] { "a", "b" });
    return tableModel;
}

private static TableModel createTableModel2() {
    DefaultTableModel tableModel = new DefaultTableModel();
    tableModel.setColumnCount(2);
    tableModel.addRow(new Object[] { "c", "d" });
    return tableModel;
}

private static void showFrame(JFrame frame) {
    frame.setSize(240, 400);
    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.setVisible(true);
}

enter image description here

Upvotes: 0

splungebob
splungebob

Reputation: 5415

EDIT: Here's a demo that will synch up the resizing of two tables that have similar columns. The idea is:

  • Create a custom TableColumnModelListener for each table's column model.
  • Upon resize, sync up the column widths. You'll have to disable the other listener temporarily, while this is happening.
  • For moving of columns, implement that logic in columnMoved(...) [left as an exercise]

This shows two-way synching:

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

public class JTableResizeColumnsDemo implements Runnable
{
  JTable table1, table2;
  TableColumnModelListener columnListener1, columnListener2;
  Map<JTable, TableColumnModelListener> map;

  public static void main(String[] args)
  {
    SwingUtilities.invokeLater(new JTableResizeColumnsDemo());
  }

  public void run()
  {
    Vector<String> names = new Vector<String>();
    names.add("One");
    names.add("Two");
    names.add("Three");

    table1 = new JTable(null, names);
    table2 = new JTable(null, names);

    columnListener1 = new ColumnChangeListener(table1, table2);
    columnListener2 = new ColumnChangeListener(table2, table1);

    table1.getColumnModel().addColumnModelListener(columnListener1);
    table2.getColumnModel().addColumnModelListener(columnListener2);

    map = new HashMap<JTable, TableColumnModelListener>();
    map.put(table1, columnListener1);
    map.put(table2, columnListener2);

    JPanel p = new JPanel(new GridLayout(2,1));
    p.add(new JScrollPane(table1));
    p.add(new JScrollPane(table2));

    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(p);
    frame.setSize(300, 200);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  class ColumnChangeListener implements TableColumnModelListener
  {
    JTable sourceTable;
    JTable targetTable;

    public ColumnChangeListener(JTable source, JTable target)
    {
      this.sourceTable = source;
      this.targetTable = target;
    }

    public void columnAdded(TableColumnModelEvent e) {}
    public void columnSelectionChanged(ListSelectionEvent e) {}
    public void columnRemoved(TableColumnModelEvent e) {}
    public void columnMoved(TableColumnModelEvent e) {}

    public void columnMarginChanged(ChangeEvent e)
    {
      TableColumnModel sourceModel = sourceTable.getColumnModel();
      TableColumnModel targetModel = targetTable.getColumnModel();
      TableColumnModelListener listener = map.get(targetTable);

      targetModel.removeColumnModelListener(listener);

      for (int i = 0; i < sourceModel.getColumnCount(); i++)
      {
        targetModel.getColumn(i).setPreferredWidth(sourceModel.getColumn(i).getWidth());
      }

      targetModel.addColumnModelListener(listener);
    }
  }
}

Upvotes: 5

Manu
Manu

Reputation: 4137

You can apply an Observer pattern: the first JTable observes the second and vice versa. Then you add listners to both tables so that, when one is "customized", the other is notified. Basically, "being notified" consists in a method invocation that causes the update of the JTable. In order to do that, you have two options:

  1. You define a class Observer with a "register" method and a "notify" method. When creating a JTable, you register it with the Observer. Then, the listener you create and associate to each JTable invoke the "notify" method of the observer, which informs all other registered JTables of the change
  2. You define a sort of "callback method" notify in the class that contains and declares the JTable. This "notify" method is invoked within the listner and updates the correct JTable. You can also create two methods: one for updating one JTable and one for the other JTable

Upvotes: 1

Related Questions