Hello good day. I am designing two JTables; one on top of the other. The first one has nine columns. And the second one has 8 columns (The first column spans the first columns of the topmost table). What I wish to do now is to make the bottom table reactive to the movement of the margins in the topmost table. I wish for them to stay aligned.

How do I accomplish this please?

I guess you could create a TableColumnModelListener to monitor changes in your JTable's TableColumnModel and get the updated widths of each TableColumn and use those to update the bottom table? A bit tortuous, but probably not very much code when you finally work it all out.

Will this result in the bottom-most table's margins moving smoothly or will the adjustment's be made in jumps? In other words, how responsive will the bottom Table be?

It should update about as fast as your pc can execute the code - normally it would be very responsive and pretty smooth I think.

Edited 3 Years Ago by JamesCherrill

Have a look at this thread that's about spanning headings across multiple columns in a JTable and keeping them synched when the table's columns are re-sized...
http://www.daniweb.com/software-development/java/threads/454696/creating-a-table-in-a-table/
... about half way down there's a little proof-of-concept I wrote to monitor column width changes and update heading widths to match. You can run it to see how responsive/smooth it is, and you may find that much of the code is directly releant to your problem, eg listening for column width changes and getting the latest widths of all the columns.
J

Edited 3 Years Ago by JamesCherrill

... just hacked that code to sync col widths from one table to a second table immediately below it.Column re-size is absolutely flawless on my ordinary Core i3 - when resizing the top table there's no visible lag or difference between the top and bottom tables, so go for it!

  • required to override (last two code lines)

myTable.doLayout()
myTable.repaint()

  • as dirty hack, sure better could be to move those methods to the XxxTableModel as method

  • all/both JTables view (in your case) have got only one ColumnModel

Hello again. I am having a problem. I fiddled with the code. But what is occuring is that the changes are becoming visible. I see the flicker which suggests that it is responding. However as soon as i flickers, it returns to its original position. Any suggestions please?

It' a bug on line 174 of your code - you have a shadowed variable name...

... but seriously - how do you expect anyone to help debug code that can't see?

Anyway, here's the code of my small runnable working demo - maybe you can see what's different?

import java.awt.*;
import static java.awt.GridBagConstraints.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

public class SynchTables {

   // Shows a second JTable keeping its col widths synched to the first
   public static void main(String... args) {
      new SynchTables();
   }

   JTable table1, table2;

   SynchTables() {
      table1 = createDemoTable1();
      table2 = createDemoTable2();
      createColumnListener();
      createDemoWindow_totallyBoring_();
   }

   JTable createDemoTable1() {
      String[] headings = {"A", "B", "C", "D"};
      String[][] data = {{"1", "2", "3", "4"}, {"5", "6", "7", "8"}};
      JTable temp = new JTable(data, headings);
      temp.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
      return temp;
   }

   JTable createDemoTable2() {
      String[] headings = {"E", "F", "G", "H"};
      String[][] data = {{"11", "12", "13", "14"}, {"15", "16", "71", "18"}};
      JTable temp = new JTable(data, headings);
      temp.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); // we will manage column widths
      return temp;
   }

   void createDemoWindow_totallyBoring_() {
      // just sets up a JFrame with the tables in it - no need to read this
      JFrame frame = new JFrame("Demo");
      frame.setLayout(new GridBagLayout()); // stack components vertically...
      GridBagConstraints gc = new GridBagConstraints(0, RELATIVE, 1, 1, 0.0, 0.0,
              NORTHWEST, HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0);
      frame.add(table1.getTableHeader(), gc);
      frame.add(table1, gc);
      frame.add(new JLabel(" "), gc); // easy space
      frame.add(table2, gc);
      frame.setMinimumSize(new Dimension(600, 200));
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);
   }

   void createColumnListener() {
      DefaultTableColumnModel colModel1 = (DefaultTableColumnModel) table1.getColumnModel();
      colModel1.addColumnModelListener(new TableColumnModelListener() {
         // just handle columnMarginChanged to re-paint headings

         @Override
         public void columnMarginChanged(ChangeEvent e) {
            // sets table2 column widths to be same as table1
            DefaultTableColumnModel colModel1 = (DefaultTableColumnModel) table1.getColumnModel();
            DefaultTableColumnModel colModel2 = (DefaultTableColumnModel) table2.getColumnModel();
            for (int cNumber = 0; cNumber < colModel1.getColumnCount(); cNumber++) {
               colModel2.getColumn(cNumber).setPreferredWidth(
                       colModel1.getColumn(cNumber).getWidth());
            }
         }

         @Override
         public void columnAdded(TableColumnModelEvent e) { }

         @Override
         public void columnRemoved(TableColumnModelEvent e) { }

         @Override
         public void columnMoved(TableColumnModelEvent e) { }

         @Override
         public void columnSelectionChanged(ListSelectionEvent e) { }
      });
   }

}

Here's the problem. The first column that I have in the bottom table, spans the first two in the uppermost table. So far the columns move, just not in unison. The upper will move 40px and the bottom will move 20 px.

So you set the width of table2 col 0 to the sum of the widths of table 1 cols 0 & 1. Then subsequent columns (i) in table 2 are set to the width of table 1 col (i-1).
Without your code, it's hard to comment on its details.

Edited 3 Years Ago by JamesCherrill

Okay sooo, which bits of my code would be relevant for you to see?

The bits where the mistake is!

But seriously - here's a runnable example that does what you said... read and learn...

import java.awt.*;
import static java.awt.GridBagConstraints.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

public class SynchTables {

   // Shows a second JTable keeping its col widths synched to the first
   public static void main(String... args) {
      new SynchTables();
   }

   JTable table1, table2;

   SynchTables() {
      table1 = createDemoTable1();
      table2 = createDemoTable2();
      synchColumns();
      createColumnListener();
      createDemoWindow_totallyBoring_();
   }

   JTable createDemoTable1() {
      String[] headings = {"A", "B", "C", "D"};
      String[][] data = {{"1", "2", "3", "4"}, {"5", "6", "7", "8"}};
      JTable temp = new JTable(data, headings);
      temp.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
      return temp;
   }

   JTable createDemoTable2() {
      String[] headings = {"E", "F", "G"};
      String[][] data = {{"11", "12", "13"}, {"15", "16", "71"}};
      JTable temp = new JTable(data, headings);
      temp.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); // we will manage column widths
      return temp;
   }

   void createDemoWindow_totallyBoring_() {
      // just sets up a JFrame with the tables in it - no need to read this
      JFrame frame = new JFrame("Demo");
      frame.setLayout(new GridBagLayout()); // stack components vertically...
      GridBagConstraints gc = new GridBagConstraints(0, RELATIVE, 1, 1, 0.0, 0.0,
              NORTHWEST, HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0);
      frame.add(table1.getTableHeader(), gc);
      frame.add(table1, gc);
      frame.add(new JLabel(" "), gc); // easy space
      frame.add(table2, gc);
      frame.setMinimumSize(new Dimension(600, 200));
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);
   }

   void synchColumns() {
      // sets table2 column widths to be same as table1
      DefaultTableColumnModel colModel1 = (DefaultTableColumnModel) table1.getColumnModel();
      DefaultTableColumnModel colModel2 = (DefaultTableColumnModel) table2.getColumnModel();
      colModel2.getColumn(0).setPreferredWidth(
              colModel1.getColumn(0).getWidth() + colModel1.getColumn(1).getWidth());
      for (int cNumber = 2; cNumber < colModel1.getColumnCount(); cNumber++) {
         colModel2.getColumn(cNumber - 1).setPreferredWidth(
                 colModel1.getColumn(cNumber).getWidth());
      }
   }

   void createColumnListener() {
      DefaultTableColumnModel colModel1 = (DefaultTableColumnModel) table1.getColumnModel();
      colModel1.addColumnModelListener(new TableColumnModelListener() {
         // just handle columnMarginChanged to re-paint headings

         @Override
         public void columnMarginChanged(ChangeEvent e) {
           synchColumns();
         }

         @Override
         public void columnAdded(TableColumnModelEvent e) { }

         @Override
         public void columnRemoved(TableColumnModelEvent e) { }

         @Override
         public void columnMoved(TableColumnModelEvent e) { }

         @Override
         public void columnSelectionChanged(ListSelectionEvent e) { }
      });
   }

}

Edited 3 Years Ago by JamesCherrill

  1. Sorry - don't understand
  2. The two tables have different numbers of columns, so they can't share one column model
  3. Yes, they should be in one scrollpane, but that won't synchronise the column widths like the OP requires
  4. The setMinimumSize on line 50 is just a quickie to make the window bigger so you can re-size the columns more easily. This is just a quick proof-of-concept runnable demo, not production code.

I solved the matter. It is solved thanks to you James Cherill. Thank you very much.

This question has already been answered. Start a new discussion instead.