3

我本着回馈的精神提交了以下实施的组件。该组件满足了我客户的要求,所以我本身并不是在寻求帮助。但是该组件提供了可能对其他人有用的有趣功能。

此外,它提出了一些关于明显必要的黑客攻击的问题,而且我的实现可能比必要的复杂得多。任何建议的替代解决方案也可能对其他人有用。

简而言之,一个大型数据表被拆分为多个 JTable,每个页面中都有一个多选项卡 JTabPane。

如果您运行该程序并以各种方式调整它的大小,您将看到该组件的行为方式。明确地,要求是:

  1. 将显示具有不同数量记录(1 到 500)的表格数据集。

  2. JTabbedPane 包含一个或多个选项卡,每个选项卡仅包含一个 JScrollPane。

  3. JScrollPanes 不能有垂直滚动条,可能有水平滚动条。

  4. 每个 JScrollPane 都包含一个 JTable。

  5. JTabbedPane 中总是有足够的选项卡来共同包含其 JTable 中的所有数据行。

  6. JTabbedPane 大小随应用程序 JFrame 大小而变化。

  7. 应用程序可以通过鼠标拖动自由调整大小。

  8. 在初始化时,无论何时调整应用程序的大小,JTabbedPane 都会重新构建,并使用足够的选项卡页来保存所有记录。例如,对于 100 条记录,如果在特定的面板大小下,选项卡的表格可以容纳 8 条记录,则创建 13 个选项卡。

  9. 如果 JTabbedPane 的大小减小到太小而无法容纳容纳所有数据行所需的选项卡数量,则不会显示任何内容(或仅显示警告)。

对 Java、MigLayout 及其交互有学术兴趣的人可能需要考虑四点:

  1. 在源代码中的某个点是以下函数调用:

    tb = tabbedPane.getBoundsAt(0);

这是一个黑客。我看不出这个呼召在世上有什么需要;然而它(或其他东西)是必要的。

  1. 从理论上讲, ScrollPane.getViewportBorderBounds() 应该给我计算标签页表格大小的信息,但我不得不破解一个值。我错了,还是在我使用它的地方返回了不正确的信息?

  2. 有一组令人眼花缭乱的函数——paint()、repaint()、validate()、invalidate()、revalidate()、update()。我发现需要在正确的时间调用特定的函数。调用顺序通常(尽管并非总是很明显)非常重要。这组函数确实可以使用严格但清晰的文档来记录它们与 AWT、Swing 以及彼此之间的交互。他们与布局管理器的交互,特别是与 MigLayout 的交互也可以使用解释。

  3. 有没有使用比我使用的方法简单得多的通用 Java 来解决需求?我重新发明轮子只是为了得到拖拉机的胎面吗?

制作: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; // arbitrary limit
  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");

    // create a temporary table of nominal size to use for table metrics
    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);

    // created scroll pane containing table, and contained in tabbed pane
    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 the entire allowed range of tabbed pages, calculate the area
    // within the tabbed pane available to hold a table.
    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();

      // we need to know how high the hz scrollbar is 
      if (!scrollbarHeightSet)
      {
        JScrollBar hzScrollBar = scrollPane.getHorizontalScrollBar();
        if (hzScrollBar != null)
          scrollbarHeight = hzScrollBar.getHeight();
        else
          scrollbarHeight = 0;
        scrollbarHeightSet = true;
      }
      // pick one
      boolean useViewport = false;
      boolean compViewport = false;
      boolean compViewport2 = true; // this one works best.

      // this presumptively correct method barely works
      if (useViewport)
      {
        viewportBounds = scrollPane.getViewportBorderBounds();
        viewable = ((int)viewportBounds.getHeight()) / dataRowHeight;
      }
      // this hack works better
      if (compViewport) 
      {
        tb = tabbedPane.getBoundsAt(0);
        bottom = (int)(tb.getY() + tb.getHeight());
        computedSpHeight = tabbedPane.getHeight() - (dataRowHeight + bottom);
        viewable = (computedSpHeight - scrollbarHeight) / dataRowHeight;
      }
      // this works well.  But what does JTabbedPane.getBoundsAt() have to do with it?
      if (compViewport2)
      {
        tb = tabbedPane.getBoundsAt(0); // !!! Worse Than Failure - this must be here!
        viewable = (scrollPane.getHeight() - scrollbarHeight) / dataRowHeight;
      }
      if (viewable > 0)
        viewable -= 1; // take out the title row

      dataRowsPerTabCount[tabIx] = viewable;
    }      
  } // getGuiMetrics

  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);

    // how many tabs are needed to display all the data rows?
    for (tabIx = 0; !accepted && tabIx < MAXTABS; ++tabIx)
    {
      rowsPerTab = dataRowsPerTabCount[tabIx];
      maxDisplayableRows = rowsPerTab * (tabIx +1);
      if (maxDisplayableRows >= dataRows)
      {
        accepted = true;
        numTabs = tabIx +1;
      }
    }
    // did we find a best fit solution?
    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");

    // create and fill the tab pages
    for (tabIx = 0, rowsAdded = 0; rowsAdded < dataRows; ++tabIx)
    {
      if (rowsAdded + rowsPerTab > dataRows)
        rowsThisTime = dataRows - rowsAdded;
      else
        rowsThisTime = rowsPerTab;

      // create the table for the page
      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);

      // fill the table with test data
      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);
        }
      }
      // create scroll pane holding table
      scrollPane = new JScrollPane(clientsTable);
      scrollPane.setVerticalScrollBarPolicy(scrollPane.VERTICAL_SCROLLBAR_NEVER);
      scrollPane.setHorizontalScrollBarPolicy(scrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

      // create tab panel holding the scroll pane
      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();
  } // updateTable

  void init(String[] args)
  {
    //  uncomment this to see the migLayout component border highlighting
    //  migDebugString = ", debug";

    // total of how many data rows?
    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();
      } // componentResized()
    }); // addComponentListener

    topPane = new JPanel(new MigLayout("fill" + migDebugString, "[100%]", "[100%]"));
    setContentPane(topPane);

    // center app window
    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();
    }
  }
} // class test
4

0 回答 0