3

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

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Ramin
  • 474
  • 6
  • 22

3 Answers3

4

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);
    }
  }
}
splungebob
  • 5,357
  • 2
  • 22
  • 45
  • Well, it does work. Simply run this self-contained demo and you'll see. Clearly, you must be doing something differently, but I can't say what since you haven't shown any code. For better help sooner, please post a [Minimal, Complete, Verifiable Example](http://stackoverflow.com/help/mcve) that demonstrates the problem. – splungebob Sep 17 '14 at 18:14
  • I don't want the data to be shared, I need the view. mainly the order of columns and their width which is modified in runtime by user and doesn't modify the underlying data – Ramin Sep 17 '14 at 18:15
  • So you want 2 tables with the same structure (column headers, etc.) but different data? If so, then what is it that you want synched? Scrolling? Column header resizing? Something else? – splungebob Sep 17 '14 at 18:29
  • column header resizing and ordering . mainly the looks. the table in the footer is supposed to be a filter row. – Ramin Sep 17 '14 at 18:51
  • @user3719495 See my EDIT in the post. – splungebob Sep 17 '14 at 19:07
1

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
Manu
  • 4,019
  • 8
  • 50
  • 94
  • I did think about this, sadly I don't know how or exactly what should be observed since there is no distinct view side to JTable. also setting them to have the same model just defines their initial layout(data + initial view) not the current view – Ramin Sep 17 '14 at 18:09
0

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

René Link
  • 48,224
  • 13
  • 108
  • 140