可水平滚动的JTable并增长以填充父容器 [英] JTable which is scrollable horizontally AND grows to fill parent container

查看:67
本文介绍了可水平滚动的JTable并增长以填充父容器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将JTable放在JScrollPane中并希望它:

I want to put a JTable in a JScrollPane and want it to:

  • 可水平和垂直滚动(仅当其内容大于父容器时才可滚动)
  • 根据需要自动增长以填充父容器(双向)
  • 在需要时仅具有一个或两个滚动条

JList的行为与此完全相同,顺便说一句.但是我发现用JTable同时实现所有目标的简便方法.完成第一种方法的简单方法是禁用自动调整大小,但是这可以防止第二种方法的发生.我提出了一个非常棘手的解决方案,该解决方案将组件侦听器添加到JScrollPane的父容器,并在调整容器大小时调整表的大小.但是,我不得不在调整大小方法中添加一些错误因素"(请参阅​​4x注释),由于一些固有的误差,我将高度/宽度检查更改为小像素量.没有这些软化因素,滚动条要么出现得太早(在需要它们之前),要么即使存在滚动条,长表格单元格的最末端也会被切断.关于这些怪异的软硬件因素,最糟糕的部分是,如果您更改了GUI的外观,那么整个解决方案就会干净地停止工作(即,软硬件因素需要更改).显然,这与理想情况相去甚远.

The JList behaves exactly like this, btw. But I have found no easy way to accomplish all goals simultaneously with a JTable. The easy way to accomplish the first is to disable auto-resizing, but that then prevents the second from occurring. I've come up with a very hacky solution which adds a component listener to the parent container of the JScrollPane and resizes the table when that container is resized. However, I've had to add some "fudge factors" (see 4x comments) to the resize method, altering my height/width checks by small pixel amounts because of some inherent inaccuracy I guess. Without these fudge factors, the scrollbars either appear too soon (before they are needed), or the very end of long table cells gets cut off even with the scrollbar present. The worst part about these hacky fudge factors is that if you change up the look and feel of the GUI, then the entire solution stops working so cleanly (i.e. the fudge factors would need to change). Which is obviously very far from ideal.

必须有一种更清洁的方法来完成此任务.有人可以帮忙吗?运行附带的代码,并拖动整个框架的大小以查看表更新.这段代码使用了金属外观,并且似乎在选择了一些软硬件因素的情况下就可以在我的Windows 7机器上运行,但是如果它不能在其他操作系统上正常运行,我也不会感到惊讶.

There has got to be a cleaner way to accomplish this. Can anybody help? Run the attached code and drag the size of the entire frame around to see the table update. This code uses the metal look and feel which seems to work on my Windows 7 machine with the fudge factors chose, but I wouldn't be surprised if it didn't work as cleanly on another OS.

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ScrollPaneConstants;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

public class JTableScrollTest {

    private JFrame frame;
    private JPanel panel;
    private DefaultTableModel tableModel;
    private JTable table;
    private JScrollPane scrollPane;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
        } catch (Throwable e) {
            e.printStackTrace();
        }

        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    JTableScrollTest window = new JTableScrollTest();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public JTableScrollTest() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        panel = new JPanel();
        frame.getContentPane().add(panel, BorderLayout.CENTER);
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        scrollPane = new JScrollPane();
        panel.add(scrollPane);

        tableModel = new DefaultTableModel(new Object[]{"Stuff"},0);
        tableModel.addRow(new Object[]{"reeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeally long string"});
        tableModel.addRow(new Object[]{"a"});
        tableModel.addRow(new Object[]{"a"});
        tableModel.addRow(new Object[]{"a"});
        tableModel.addRow(new Object[]{"a"});
        tableModel.addRow(new Object[]{"a"});
        tableModel.addRow(new Object[]{"a"});
        tableModel.addRow(new Object[]{"a"});
        tableModel.addRow(new Object[]{"a"});
        tableModel.addRow(new Object[]{"a"});
        tableModel.addRow(new Object[]{"a"});
        tableModel.addRow(new Object[]{"a"});
        tableModel.addRow(new Object[]{"LAST ITEM"});
        table = new JTable(tableModel);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        scrollPane.setViewportView(table);

        resizeTable();
        panel.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent evt) {
                resizeTable();
            }
        });
    }

    public void resizeTable() {
        int width = 0;
        int height = 0;
        for (int row = 0; row < table.getRowCount(); row++) {
            TableCellRenderer renderer = table.getCellRenderer(row, 0);
            Component comp = table.prepareRenderer(renderer, row, 0);
            width = Math.max (comp.getPreferredSize().width, width);
            height += comp.getPreferredSize().height;
        }
        if (width > panel.getWidth() - 2) { // fudge factor
            width += 4; // fudge factor
            scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        } else {
            width = panel.getWidth();
            scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        }
        if (height > panel.getHeight() + 4) { // fudge factor
            height -= 26; // fudge factor
            scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        } else {
            height = panel.getHeight();
            scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
        }
        table.setPreferredSize(new Dimension(width, height));
    }
}

推荐答案

我遇到了同样的问题.该表只是使列变大或变小,而是显示滚动条.通过反复试验,我想出的骇客是您附加到ScrollPaneComponentListener.

I had the same issue. The table just makes the columns larger or smaller rather that show a scrollbar. Through trial and error, the hack I came up with is a ComponentListener that you attach to the ScrollPane.

诀窍是,一旦滚动窗格小于表的首选大小,就可以关闭表的自动调整大小.请注意,您不应该直接设置表格的首选大小.不用理会自动进行计算.如果需要某些特定的列,请通过列模型(例如table.getColumnModel().getColumn(0).setMaxWidth(25))设置其宽度.

The trick is to turn off the table's auto resize once the scroll pane is smaller than the table's preferred size. Note that you shouldn't set the table's preferred size directly. Leave that alone to be automatically calculated. If you need certain columns larger or smaller, set their width via the column model (e.g. table.getColumnModel().getColumn(0).setMaxWidth(25)).

该类还包括另一个TableModelListener,当将列添加到表中时,该TableModelListener会执行相同的逻辑.

This class also includes another TableModelListener that performs the same logic when columns are added to the table.

public final class ScrollingTableFix implements ComponentListener {
  private final JTable table;

  public ScrollingTableFix(JTable table, JScrollPane scrollPane) {
    assert table != null;

    this.table = table;

    table.getModel().addTableModelListener(new ColumnAddFix(table, scrollPane));
  }

  public void componentHidden(final ComponentEvent event) {}

  public void componentMoved(final ComponentEvent event) {}

  public void componentResized(final ComponentEvent event) {
    // turn off resize and let the scroll bar appear once the component is smaller than the table
    if (event.getComponent().getWidth() < table.getPreferredSize().getWidth()) {
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    }
    // otherwise resize new columns in the table
    else {
        table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
    }
  }

  public void componentShown(final ComponentEvent event) {}

  // similar behavior is needed when columns are added to the table
  private static final class ColumnAddFix implements TableModelListener {
    private final JTable table;
    private final JScrollPane scrollPane;

    ColumnAddFix(JTable table, JScrollPane scrollPane) {
      this.table = table;
      this.scrollPane = scrollPane;
     }

    @Override
    public void tableChanged(TableModelEvent e) {
      if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
        if (scrollPane.getViewport().getWidth() < table.getPreferredSize().getWidth()) {
          table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        }
        else {
          table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        }
      }
     }
   }
}

要使用此功能,只需将所有内容连接起来,就像这样:

To use this, simply wire everything up like so:

JTable table = new JTable();
JScrollPane scroller = new JScrollPane(table);
scroller.addComponentListener(new ScrollingTableFix(table, scroller));

这篇关于可水平滚动的JTable并增长以填充父容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆