我想把JTable
放到JScrollPane
中,并希望它:
- 可以水平和垂直滚动(只有当它的内容比父容器大时) 如果需要,
- 自动生长以填充父容器(在两个方向)
- 只在需要的时候有一个或两个滚动条
顺便说一句,JList
的行为完全是这样的。但我没有找到一个简单的方法来同时完成所有的目标与JTable
。实现第一个目标的简单方法是禁用自动调整大小,但这会阻止第二个目标的实现。我想出了一个非常hack的解决方案,它添加了一个组件监听器到JScrollPane
的父容器,并在容器调整大小时调整表的大小。然而,我不得不在调整大小方法中添加一些"模糊因素"(参见4x评论),因为我认为某些固有的不准确性,所以我将高度/宽度检查更改为小像素量。如果没有这些模糊因素,滚动条要么出现得太快(在需要它们之前),要么长表格单元格的最后被截断,即使滚动条存在。最糟糕的是,如果你改变了GUI的外观和感觉,那么整个解决方案就会停止运行(游戏邦注:即需要改变蒙混因素)。这显然与理想相差甚远。
必须有一个更干净的方法来完成这个。有人能帮忙吗?运行附带的代码并拖动整个框架的大小,以查看表的更新情况。这段代码使用了金属的外观和感觉,似乎在我的Windows 7机器上使用了软糖因素,但如果它在其他操作系统上运行得不那么干净,我也不会感到惊讶。
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));
}
}
我也有同样的问题。表格只是使列变大或变小,而不是显示滚动条。通过反复试验,我想出的破解方法是将ComponentListener
附加到ScrollPane
上。
技巧是在滚动窗格小于表的首选大小时关闭表的自动调整大小。注意,不应该直接设置表的首选大小。让它自己自动计算吧。如果您需要某些列更大或更小,可以通过列模型设置它们的宽度(例如table.getColumnModel().getColumn(0).setMaxWidth(25)
)。
这个类还包括另一个TableModelListener
,它在向表中添加列时执行相同的逻辑。
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);
}
}
}
}
}
要使用它,只需像这样连接所有内容:
JTable table = new JTable();
JScrollPane scroller = new JScrollPane(table);
scroller.addComponentListener(new ScrollingTableFix(table, scroller));