I present the following implemented component in the spirit of bestowal. The component meets my customer requirements, so I do not ask for help as such. But the component provides interesting features that may be useful to others.
In addition, there are some questions about clearly needed hacks, and it is possible that my implementation is terribly more complicated than necessary. Any proposed alternative solutions may be useful to others.
In a nutshell, a large data table is divided into several JTables, one on each page - a multitask JTabPane.
If you run the program and resize it differently, you will see how this component should work. Obviously, the requirements are:
A tabular data set with a different number of records (from 1 to 500) should be shown.
JTabbedPane contains one or more tabs, each of which contains only JScrollPane.
JScrollPanes cannot have vertical scrollbars, can have horizontal scrollbars.
Each JScrollPane contains a JTable.
JTabbedPane always has enough tabs to collectively store all rows of data in their JTables.
The size of the JTabbedPane depends on the size of the JFrame application.
The application can be freely modified by drag and drop.
During initialization and when resizing the application, JTabbedPane is rebuilt with just tabs to store all records. For instance. for 100 entries, if at a certain panel size 8 entries can be stored in the tabulation table, then 13 tabs are created.
JTabbedPane , , , , ( ).
Java, MigLayout :
. , ; ( -) .
ScrollPane.getViewportBorderBounds() , . , , ?
- paint(), repaint(), validate(), invalidate(), revalidate(), update(). , . , , . , AWT, Swing . MigLayout, , .
Java, , , ? , ?
make: javac -classpath ScrollTableTest.java
: java -classpath ScrollTableTest [ ]
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.*;
import java.io.*;
import java.text.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.filechooser.*;
import javax.swing.table.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableColumn;
import net.miginfocom.swing.MigLayout;
public class ScrollTableTest
extends JFrame
{
public final static int APPWIDTH = 500;
public final static int APPHEIGHT = 300;
public final static String[] CLIENT_COL_NAMES = { "Col 1", "Col 2", "Col 3", "Col 4" };
public final static int COLS = CLIENT_COL_NAMES.length;
public final static int MAXTABS = 50;
public final static int arbitraryTweek1 = 20;
String migDebugString = "";
int[] dataRowsPerTabCount = new int [MAXTABS];
JPanel topPane = null;
DefaultTableModel clientsTableModel;
String[][] clientData;
JScrollPane scrollPane;
Rectangle viewportBounds;
JTable clientsTable;
JTabbedPane tabbedPane;
int dataRows, maxVisibleRow = -1;
int rowsToShow = 1;
int dataRowHeight;
void printBasics()
{
if (scrollPane == null)
return;
System.out.println("");
System.out.println("clientsTable height " + clientsTable.getHeight());
System.out.println("topPane height: " + topPane.getHeight());
System.out.println("tabbedPane height " + tabbedPane.getHeight());
System.out.println("scrollPane height: " + scrollPane.getHeight());
System.out.println("viewport bounds: y " + viewportBounds.getY() +
" height " + (int)viewportBounds.getHeight());
}
void printDims()
{
printBasics();
double diff = viewportBounds.getHeight() - clientsTable.getHeight();
System.out.println("dataRowHeight: " + dataRowHeight);
System.out.println("differential: " + diff);
}
void getGuiMetrics()
{
double diff;
Rectangle tb;
int clientRows = 20;
int viewable = 0;
int bottom;
int computedSpHeight;
int tabIx;
boolean scrollbarHeightSet = false;
int scrollbarHeight = 0;
String title;
topPane = new JPanel(new MigLayout("fill" + migDebugString, "[100%]", "[100%]"));
setContentPane(topPane);
validate();
tabbedPane = new JTabbedPane();
topPane.add(tabbedPane, "cell 0 0, grow");
clientData = new String[clientRows][COLS];
clientsTableModel = new DefaultTableModel(clientData, CLIENT_COL_NAMES);
clientsTable = new JTable(clientRows, COLS);
clientsTable.setModel(clientsTableModel);
clientsTable.setPreferredScrollableViewportSize(null);
clientsTable.getTableHeader().setReorderingAllowed(false);
clientsTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
clientsTable.getSelectionModel().setSelectionInterval(0, 0);
scrollPane = new JScrollPane(clientsTable);
scrollPane.setVerticalScrollBarPolicy(scrollPane.VERTICAL_SCROLLBAR_NEVER);
scrollPane.setHorizontalScrollBarPolicy(scrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
tabbedPane.setMaximumSize(new Dimension(topPane.getWidth(), topPane.getHeight() - arbitraryTweek1));
for (tabIx = 0; tabIx < MAXTABS; ++tabIx)
{
JPanel panel = new JPanel(new MigLayout("fill" + migDebugString, "[100%]", "[100%]"));
title = "Page " + (tabIx +1);
tabbedPane.addTab(title, panel);
panel.add(scrollPane, "cell 0 0, grow");
if (tabIx == 0)
{
validate();
dataRowHeight = clientsTable.getHeight() / clientRows;
}
else
tabbedPane.revalidate();
if (!scrollbarHeightSet)
{
JScrollBar hzScrollBar = scrollPane.getHorizontalScrollBar();
if (hzScrollBar != null)
scrollbarHeight = hzScrollBar.getHeight();
else
scrollbarHeight = 0;
scrollbarHeightSet = true;
}
boolean useViewport = false;
boolean compViewport = false;
boolean compViewport2 = true;
if (useViewport)
{
viewportBounds = scrollPane.getViewportBorderBounds();
viewable = ((int)viewportBounds.getHeight()) / dataRowHeight;
}
if (compViewport)
{
tb = tabbedPane.getBoundsAt(0);
bottom = (int)(tb.getY() + tb.getHeight());
computedSpHeight = tabbedPane.getHeight() - (dataRowHeight + bottom);
viewable = (computedSpHeight - scrollbarHeight) / dataRowHeight;
}
if (compViewport2)
{
tb = tabbedPane.getBoundsAt(0);
viewable = (scrollPane.getHeight() - scrollbarHeight) / dataRowHeight;
}
if (viewable > 0)
viewable -= 1;
dataRowsPerTabCount[tabIx] = viewable;
}
}
void updateTable()
{
int tabIx, numTabs, rowsPerTab = 0, maxDisplayableRows = 0, rowsAdded, rowsThisTime;
boolean accepted = false;
getGuiMetrics();
topPane = new JPanel(new MigLayout("fill" + migDebugString, "[100%]", "[100%]"));
setContentPane(topPane);
for (tabIx = 0; !accepted && tabIx < MAXTABS; ++tabIx)
{
rowsPerTab = dataRowsPerTabCount[tabIx];
maxDisplayableRows = rowsPerTab * (tabIx +1);
if (maxDisplayableRows >= dataRows)
{
accepted = true;
numTabs = tabIx +1;
}
}
if (!accepted)
{
topPane.add(new JLabel("Not enough space for all data rows"));
return;
}
tabbedPane = new JTabbedPane();
validate();
tabbedPane.setMaximumSize(new Dimension(topPane.getWidth(), topPane.getHeight() - arbitraryTweek1));
topPane.add(tabbedPane, "cell 0 0, grow");
for (tabIx = 0, rowsAdded = 0; rowsAdded < dataRows; ++tabIx)
{
if (rowsAdded + rowsPerTab > dataRows)
rowsThisTime = dataRows - rowsAdded;
else
rowsThisTime = rowsPerTab;
clientData = new String[rowsThisTime][COLS];
clientsTableModel = new DefaultTableModel(clientData, CLIENT_COL_NAMES);
clientsTable = new JTable(rowsThisTime, COLS);
clientsTable.setModel(clientsTableModel);
clientsTable.setPreferredScrollableViewportSize(null);
clientsTable.getTableHeader().setReorderingAllowed(false);
clientsTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
clientsTable.getSelectionModel().setSelectionInterval(0, 0);
for (int row = 0; row < rowsThisTime; ++row)
{
for (int col = 0; col < COLS; ++col)
{
String cellVal = "tab " + (tabIx +1) + " cell row " + (row+1) + " col " + (col+1);
clientsTableModel.setValueAt(cellVal, row, col);
}
}
scrollPane = new JScrollPane(clientsTable);
scrollPane.setVerticalScrollBarPolicy(scrollPane.VERTICAL_SCROLLBAR_NEVER);
scrollPane.setHorizontalScrollBarPolicy(scrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
JPanel panel = new JPanel(new MigLayout("fill" + migDebugString, "[100%]", "[100%]"));
String title = "Page " + (tabIx +1);
tabbedPane.addTab(title, panel);
panel.add(scrollPane, "cell 0 0, grow");
rowsAdded += rowsPerTab;
}
tabbedPane.revalidate();
}
void init(String[] args)
{
if (args.length < 1)
{
dataRows = 20;
}
else
{
dataRows = Integer.valueOf(args[0]);
if (dataRows <= 0)
{
System.out.println("bad arg");
System.exit(0);
}
}
setSize(APPWIDTH, APPHEIGHT);
addComponentListener(new ComponentAdapter()
{
public void componentShown(ComponentEvent evt)
{
}
public void componentHidden(ComponentEvent evt)
{
}
public void componentResized(ComponentEvent evt)
{
updateTable();
}
});
topPane = new JPanel(new MigLayout("fill" + migDebugString, "[100%]", "[100%]"));
setContentPane(topPane);
GraphicsConfiguration gc = getGraphicsConfiguration();
Rectangle bounds = gc.getBounds();
setLocation((int)((bounds.width-APPWIDTH) /2),
(int)((bounds.height - APPHEIGHT) /2));
setVisible(true);
}
public static void main(String[] args)
{
try
{
ScrollTableTest thisTest = new ScrollTableTest();
thisTest.init(args);
}
catch (Exception e)
{
System.out.println("runTest caught exception: " + e.getMessage());
e.printStackTrace();
}
}
}