Java 布局JScrollPane时何时调用getPreferredScrollableViewportSize()?
实现可滚动接口需要实现getPreferredScrollableViewportSize()方法。这通常是通过转发对getPreferredSize()的调用来完成的-除非可滚动的其他参数可能会影响首选的JViewport大小,例如JTree中的setVisibleRowCount()方法 我认为这个方法可以帮助我实现我的目标,但是我的getPreferredScrollableViewportSize()实现中的一个简单的print语句证实了它从未被调用过。搜索JScrollPane、ScrollPaneLayout和JViewport确认没有(直接)调用该方法。然而,JScrollPane中的注释明确指出ScrollPanelLayout使用了它,我可以确认它在JTree中的实现与预期一致Java 布局JScrollPane时何时调用getPreferredScrollableViewportSize()?,java,swing,jscrollpane,scrollable,jviewport,Java,Swing,Jscrollpane,Scrollable,Jviewport,实现可滚动接口需要实现getPreferredScrollableViewportSize()方法。这通常是通过转发对getPreferredSize()的调用来完成的-除非可滚动的其他参数可能会影响首选的JViewport大小,例如JTree中的setVisibleRowCount()方法 我认为这个方法可以帮助我实现我的目标,但是我的getPreferredScrollableViewportSize()实现中的一个简单的print语句证实了它从未被调用过。搜索JScrollPane、Scr
它是什么时候调用的,由哪个类(大概是LayoutManager)调用,以及何时调用?我使用的是JDK1.7_07,我还没有时间搜索所有的源代码,但是在测试中,似乎在打包GUI时调用了该方法
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
@SuppressWarnings("serial")
public class TestScrollable extends JPanel {
private static final int REPACK_COUNT = 10;
protected static final int RESIZE_COUNT = 5;
public TestScrollable() {
MyScrollable mainScrollable = new MyScrollable("Main Scrollable");
mainScrollable.setLayout(new GridLayout(0, 1));
int rowCount = 100;
for (int i = 0; i < rowCount; i++) {
JPanel rowPanel = new JPanel();
String name = "Row Panel " + i;
rowPanel.setName(name);
rowPanel.setBorder(BorderFactory.createLineBorder(Color.blue));
rowPanel.setLayout(new BorderLayout());
rowPanel.add(new JLabel(rowPanel.getName()));
mainScrollable.add(rowPanel);
}
JViewport viewport = new JViewport() {
@Override
public void doLayout() {
System.out.println("viewport doLayout called");
super.doLayout();
}
};
viewport.setView(mainScrollable);
JScrollPane scrollPane = new JScrollPane() {
@Override
public void doLayout() {
System.out.println("scrollpane doLayout called");
super.doLayout();
}
};
scrollPane.setViewport(viewport);
setLayout(new BorderLayout());
add(scrollPane);
}
private static void createAndShowGui() {
TestScrollable mainPanel = new TestScrollable();
final JFrame frame = new JFrame("TestScrollable") {
@Override
public void pack() {
System.out.println("JFrame pack() called");
super.pack();
}
};
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
int delay = 1000;
// re-test pack()
new Timer(delay, new ActionListener() {
private int timerCount = 0;
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("timer count: " + timerCount);
if (timerCount == RESIZE_COUNT) {
int newWidth = frame.getSize().width * 2;
int newHeight = frame.getSize().height * 2;
Dimension newSize = new Dimension(newWidth, newHeight);
frame.setSize(newSize);
frame.repaint();
}
if (timerCount == REPACK_COUNT) {
System.out.println("calling pack again");
frame.pack();
((Timer) e.getSource()).stop();
}
timerCount++;
}
}).start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
@SuppressWarnings("serial")
class MyScrollable extends JComponent implements Scrollable {
public static final int VP_WIDTH = 600;
private static final int ROW_COUNT = 10;
public MyScrollable(String name) {
super.setName(name);
}
@Override
public Dimension getPreferredScrollableViewportSize() {
System.out.println(getName()
+ " getPreferredScrollableViewportSize called");
Component[] comps = getComponents();
if (comps.length > 0) {
int height = ROW_COUNT * comps[0].getPreferredSize().height;
return new Dimension(VP_WIDTH, height);
}
return super.getPreferredSize();
}
@Override
public Dimension getPreferredSize() {
System.out.println(getName() + " getPreferredSize called");
return super.getPreferredSize();
}
@Override
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction) {
if (orientation == SwingConstants.HORIZONTAL) {
return VP_WIDTH;
}
Component[] comps = getComponents();
if (comps.length > 0) {
return comps[0].getHeight() * (3 * ROW_COUNT / 4);
}
return getSize().height / 3;
}
@Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
@Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation, int direction) {
if (orientation == SwingConstants.HORIZONTAL) {
return VP_WIDTH;
}
Component[] comps = getComponents();
if (comps.length > 0) {
return comps[0].getHeight();
}
return getSize().height / 3;
}
}
编辑2当我将getPreferredScrollableViewportSize覆盖更改为:
@Override
public Dimension getPreferredScrollableViewportSize() {
System.out.println(getName()
+ " getPreferredScrollableViewportSize called");
StackTraceElement[] foo = Thread.currentThread().getStackTrace();
int maxTraces = 10;
for (int i = 0; i < foo.length && i < maxTraces ; i++) {
System.out.printf("%02d: %s%n", i, foo[i]);
}
if (getComponentCount() > 0) {
Component[] comps = getComponents();
int height = ROW_COUNT * comps[0].getPreferredSize().height;
return new Dimension(VP_WIDTH, height);
}
return super.getPreferredSize();
}
建议ViewportLayout类的preferredLayoutSize调用Scrollable的getPreferredScrollableViewportSize方法
编辑3事实上,政府支持这一点:
86 public Dimension preferredLayoutSize(Container parent) {
87 Component view = ((JViewport)parent).getView();
88 if (view == null) {
89 return new Dimension(0, 0);
90 }
91 else if (view instanceof Scrollable) {
92 return ((Scrollable)view).getPreferredScrollableViewportSize();
93 }
94 else {
95 return view.getPreferredSize();
96 }
97 }
请参见我的答案的编辑2。看来是ViewportLayout调用了您的方法。@HovercraftFullOfEels干得不错,谢谢。是的,看起来确实如此。与JScrollPane直接将ScrollPanelLayout设置为其LayoutManager不同,JViewport不硬编码ViewportLayout,而是依赖JComponent查询其UI,该UI决定了JViewport的LayoutManager。我还没有完全了解整个UI,所以这可能不是完全准确的。
Main Scrollable getPreferredScrollableViewportSize called
00: java.lang.Thread.getStackTrace(Unknown Source)
01: pkg.MyScrollable.getPreferredScrollableViewportSize(TestScrollable.java:115)
02: javax.swing.ViewportLayout.preferredLayoutSize(Unknown Source)
03: java.awt.Container.preferredSize(Unknown Source)
04: java.awt.Container.getPreferredSize(Unknown Source)
05: javax.swing.JComponent.getPreferredSize(Unknown Source)
06: javax.swing.ScrollPaneLayout.preferredLayoutSize(Unknown Source)
07: java.awt.Container.preferredSize(Unknown Source)
08: java.awt.Container.getPreferredSize(Unknown Source)
09: javax.swing.JComponent.getPreferredSize(Unknown Source)
86 public Dimension preferredLayoutSize(Container parent) {
87 Component view = ((JViewport)parent).getView();
88 if (view == null) {
89 return new Dimension(0, 0);
90 }
91 else if (view instanceof Scrollable) {
92 return ((Scrollable)view).getPreferredScrollableViewportSize();
93 }
94 else {
95 return view.getPreferredSize();
96 }
97 }