0

I'd like to have x JTables on top of each other acting like one JTable. I have created x separate JScollPanes - one for each table and inserted them all into one JPanel (panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));).

I only show the header for the top most table. In order to achieve them scrolling horizontally in unison, I have used here.

I need two more tasks left to achieve the desired result of it looking like one big JTable.

  1. I'd like there to only be one vertical scroll bar? How to achieve this?

  2. I'd like all tables to share the same TableColumnModel as the top most table (which is the only one that shows the header) so if a column is moved via drag drop header, all tables reflect the change...

In case anyone is wondering, I previously tried to add each JTable to a JPanel and then add each JPanel to one JScrollPane. The problem with that solution is that if one of the JTable's has many entries, a vertical scroll bar won't appear to show all the entries....

TT.
  • 15,774
  • 6
  • 47
  • 88
oscar
  • 3
  • 3
  • I am not sure what you are trying to accomplish, but your approach sounds very weird and outside of the scope of what the JTable is actually intended to do. Why do you even want to use multiple JTables, rather than just one? –  May 27 '20 at 19:34
  • 1
    @Taschi: I think the OP is looking for a component commonly known as an accordion. There's no Swing component that does what the OP wishes; however, he could create one or find one on the Internet. – Gilbert Le Blanc May 27 '20 at 20:46

2 Answers2

1

Based on this question and pictures from your other questions I might have an approach, although it is a bit of a hack.

The approach:

  1. Create multiple tables and add them to a JScrollPane. Add each scroll pane to the panel using the BoxLayout

  2. The first scroll pane will contain and empty table so only the table header will be visible in the scroll pane.

  3. The last scroll pane will display the JTable and the horizontal scrollbar.

  4. Any scroll pane between the first and last will only display the JTable.

  5. The code uses a custom ScrollablePanel. This panel will force the width of the panel to be equal to the width of the viewport. This will in turn force the horizontal scroll bars of each scroll pane to be active. Each horizontal scrollbar shares the same model, so even though only one scrollbar is visible all tables will scroll at the same time.

Check out Scrollable Panel for the code to download.

Here the code:

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

public class TableSharedHeader2 extends JPanel
{
    private JTable table1;
    private JTable table2;
    private JPanel tablePanel;

    TableSharedHeader2()
    {
        setLayout( new BorderLayout() );

        //  Only the Table Header is displayed

        JTable table0 = new JTable( 0, 10);
        table0.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
        table0.setPreferredScrollableViewportSize(table0.getPreferredSize());
        JScrollPane scrollPane0 = new JScrollPane( table0 );
        scrollPane0.setVerticalScrollBarPolicy( ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER );

        //  Only the JTable is displayed

        table1 = new JTable(5, 10);
        table1.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
        table1.setTableHeader( null );
        table1.setPreferredScrollableViewportSize(table1.getPreferredSize());
        JScrollPane scrollPane1 = new JScrollPane( table1 );
        scrollPane1.setVerticalScrollBarPolicy( ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER );

        // The JTable and the horizontal scrollbar is displayed.

        table2 = new JTable(3, 10);
        table2.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
        table2.setTableHeader( null );
        System.out.println(table0.getTableHeader());
        table2.setPreferredScrollableViewportSize(table2.getPreferredSize());
        JScrollPane scrollPane2 = new JScrollPane( table2 );
        scrollPane2.setVerticalScrollBarPolicy( ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER );
        scrollPane2.setHorizontalScrollBarPolicy( ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS );

        // share the column model with the last two tables

        table1.setColumnModel( table0.getColumnModel() );
        table2.setColumnModel( table0.getColumnModel() );

        // share the scrollbar model with the last two scrollbars

        scrollPane1.getHorizontalScrollBar().setModel( scrollPane0.getHorizontalScrollBar().getModel());
        scrollPane2.getHorizontalScrollBar().setModel( scrollPane0.getHorizontalScrollBar().getModel());

        //  hide the scrollbars of the first two tables.

        scrollPane0.getHorizontalScrollBar().setPreferredSize( new Dimension(0, 0) );
        scrollPane1.getHorizontalScrollBar().setPreferredSize( new Dimension(0, 0) );

        // add components to the panel

        tablePanel = new JPanel();
        ScrollablePanel tablePanel = new ScrollablePanel();
        tablePanel.setScrollableWidth( ScrollablePanel.ScrollableSizeHint.FIT );
        tablePanel.setLayout( new BoxLayout(tablePanel, BoxLayout.Y_AXIS) );
        tablePanel.add( scrollPane0 );
        tablePanel.add( new JLabel("First Label") );
        tablePanel.add( scrollPane1 );
        tablePanel.add( new JLabel("Second Label") );
        tablePanel.add( scrollPane2 );

        JScrollPane scrollPane = new JScrollPane( tablePanel );
        scrollPane.setHorizontalScrollBarPolicy( ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
        add( scrollPane );

    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("TableSharedHeader2");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new TableSharedHeader2());
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }
}
camickr
  • 321,443
  • 19
  • 166
  • 288
  • Thank you for your help. We are getting very close. I tried your code and it looks great except for two issues... 1.) The header disappears on vertical scroll and 2.) when using mouse wheel to scroll the vertical scroll bar isin't automatically activated on the scrollable panel...
    any ideas how to solve these 2 issues?
    – oscar May 28 '20 at 02:49
  • *We are getting very close* - so what have you tried? 1) The first table should just be used to provide the header and the scrollbar. Then you can move the table header to the BorderLayout.PAGE_START and the scrollbar to the BorderLayout.PAGE_END of the panel since it is using a BorderLayout. The two other table can then be configured the same way, since only the JTable itself will be added to the tablePanel. 2) the scroll wheel scrolls the scrollpane under the mouse. – camickr May 28 '20 at 04:49
  • There is one small issue.... If the data all fits and you stretch the frame vertically, white(gray) space is created between components listed in BorderFactory.CENTER... Anyway to stop that from happening? – – oscar May 28 '20 at 11:04
0

Here is the code solution after great help and guidance from camickr.

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

public class TableSharedHeader2 extends JPanel
{
    private JTable table1;
    private JTable table2;
    private JPanel tablePanel;

    TableSharedHeader2()
    {
        setLayout( new BorderLayout() );

        //  Only the Table Header is displayed

        JTable table0 = new JTable( 0, 10);
        table0.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
        table0.setPreferredScrollableViewportSize(table0.getPreferredSize());
        JScrollPane scrollPane0 = new JScrollPane( table0 );
        scrollPane0.setVerticalScrollBarPolicy( ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER );

        //  Only the JTable is displayed

        table1 = new JTable(5, 10);
        table1.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
        table1.setTableHeader( null );
        table1.setPreferredScrollableViewportSize(table1.getPreferredSize());
        JScrollPane scrollPane1 = new JScrollPane( table1 );
        scrollPane1.setVerticalScrollBarPolicy( ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER );

        // The JTable and the horizontal scrollbar is displayed.

        table2 = new JTable(60, 10);
        table2.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
        table2.setTableHeader( null );
        System.out.println(table0.getTableHeader());
        table2.setPreferredScrollableViewportSize(table2.getPreferredSize());
        JScrollPane scrollPane2 = new JScrollPane( table2 );
        scrollPane2.setVerticalScrollBarPolicy( ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER );
         scrollPane2.setHorizontalScrollBarPolicy( ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );

        // share the column model with the last two tables

        table1.setColumnModel( table0.getColumnModel() );
        table2.setColumnModel( table0.getColumnModel() );

        // share the scrollbar model with the last two scrollbars

        scrollPane1.getHorizontalScrollBar().setModel( scrollPane0.getHorizontalScrollBar().getModel());
        scrollPane2.getHorizontalScrollBar().setModel( scrollPane0.getHorizontalScrollBar().getModel());

        //  hide the scrollbars of the first two tables.

        scrollPane0.getHorizontalScrollBar().setPreferredSize( new Dimension(0, 0) );
        scrollPane1.getHorizontalScrollBar().setPreferredSize( new Dimension(0, 0) );

        // add components to the panel

        tablePanel = new JPanel();
        ScrollablePanel tablePanel = new ScrollablePanel();
        tablePanel.setScrollableWidth( ScrollablePanel.ScrollableSizeHint.FIT );

        //changed this to stretch for Vertical Scroll Bar to appear if frame is resized and data can not fit in viewport
        tablePanel.setScrollableHeight( ScrollablePanel.ScrollableSizeHint.STRETCH );
        tablePanel.setLayout( new BoxLayout(tablePanel, BoxLayout.Y_AXIS) );

        tablePanel.add( new JLabel("First Label") );
        tablePanel.add( scrollPane1 );
        tablePanel.add( new JLabel("Second Label") );
        tablePanel.add( scrollPane2 );

        JScrollPane scrollPane = new JScrollPane( tablePanel );
        JScrollBar bar = scrollPane2.getHorizontalScrollBar();

    //this removes mouse wheel listeners from all the inner scrollpanes and
   //allows the main scrollpane (scrollPane) to react to mousewheel
        scrollPane0.removeMouseWheelListener(scrollPane0.getMouseWheelListeners()[0]);
        scrollPane1.removeMouseWheelListener(scrollPane1.getMouseWheelListeners()[0]);
        scrollPane2.removeMouseWheelListener(scrollPane2.getMouseWheelListeners()[0]);

        // Add header to top of border layout
        add(scrollPane0,BorderLayout.PAGE_START);

        //add main tablePanel which has JTables (no headers) and labels to body of border layout
        add( scrollPane,BorderLayout.CENTER );

        //add bottom scollpane (scrollpane2) scroll bar to bottom of border layout 
        add(bar,BorderLayout.PAGE_END);
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("TableSharedHeader2");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new TableSharedHeader2());
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }
}
oscar
  • 3
  • 3
  • There is one small issue.... If the data all fits and you stretch the frame vertically, white(gray) space is created between components listed in BorderFactory.CENTER... Anyway to stop that from happening? – oscar May 28 '20 at 07:03
  • Perhaps worthy to ask a new question? Or ask it in comment section on camickr's answer so that he gets an alert on your question there. – TT. May 28 '20 at 08:46
  • *the data all fits and you stretch the frame vertically, white(gray) space is created between components listed in BorderFactory.CENTER.* - This is a layout issue. Don't use BorderLayout for the top level layout. Maybe try another vertical BoxLayout? – camickr May 28 '20 at 14:08
  • Which layout(s) ensure the column header is frozen on top? ( Meaning they have a PAGE_START and PAGE_END attribute or something similar) – oscar May 28 '20 at 23:55