我已经在我的 JFrame 中覆盖了 validate() 以便我可以手动控制几个嵌套的 JPanel 的大小(它们用于将内容滚动到屏幕上,并且我所知道的布局管理器没有让您在边界之外布置组件父容器)。这在拖动窗口以调整其大小时可以正常工作,但是当单击“最大化”按钮时,将调用 validate(),调用 setPreferredSize(),但面板大小不会更新。在 XP 上看到的问题,在 OSX 中没有看到。
public void validate() {
super.validate();
LOGGER.debug("Validate called on Frame. Resizing panel");
if (inited == true) {
Dimension size = panelLeft.getSize();
int referenceHeight = size.height;
LOGGER.info("referenceHeight is " + referenceHeight);
size = lower.getSize();
size.height = referenceHeight;
lower.setPreferredSize(size);
lower.setMinimumSize(size);
size.height = size.height * 2;
movingPanel.setPreferredSize(size);
movingPanel.setMinimumSize(size);
LOGGER.info("sizes now: panel: " + lower.getSize().height + ", scrollpane: "
+ movingPanel.getSize().height);
if (panelSlideController != null) {
panelSlideController.redraw();
LOGGER.debug("redrawing panel slide controller");
}
}
}
panelLeft 是一个面板,由其布局管理器自动调整大小为框架的全高。所以它的高度被用作参考。
相关面板布置:
----------------------------------
|scrollPane |
| -------------------------------- |
||movingPanel ||
|| ------------------------------ ||
|||upper |||
||| |||
||| |||
||| |||
|| ------------------------------ ||
|| ------------------------------ ||
|||lower |||
||| |||
||| |||
||| |||
|| ------------------------------ ||
| -------------------------------- |
----------------------------------
用户只能看到此布局的上半部分。MovingPanel JPanel 位于 JScrollPane 的视口中。目标是保持上面板占据所有可见的垂直空间,因此与封闭的 JFrame 高度大致相同。下面板已准备好滚动并保持与上面板相同的高度。通过保持lower.height == panelLeft.height 和movingPanel.height == panelLeft.heightx2,这意味着movingPanel 的一半正在显示,而上端位于Frame 的底部。
就像我说的,工作正常。拖动窗口时,一些示例输出为:
2013-06-20 23:15:41,298 [WT-EventQueue-0] DEBUG s.billing.ui.Form - Validate called on Frame. Resizing panel
2013-06-20 23:15:41,298 [WT-EventQueue-0] INFO s.billing.ui.Form - newheight is 617
2013-06-20 23:15:41,298 [WT-EventQueue-0] INFO s.billing.ui.Form - sizes now: panel: 607, scrollpane: 1214
2013-06-20 23:15:41,538 [WT-EventQueue-0] DEBUG s.billing.ui.Form - Validate called on Frame. Resizing panel
2013-06-20 23:15:41,538 [WT-EventQueue-0] INFO s.billing.ui.Form - newheight is 640
2013-06-20 23:15:41,538 [WT-EventQueue-0] INFO s.billing.ui.Form - sizes now: panel: 636, scrollpane: 1272
最大化窗口时,输出如下:
2013-06-20 22:08:21,234 [WT-EventQueue-0] DEBUG s.billing.ui.Form - Validate called on Frame. Resizing panel
2013-06-20 22:08:21,234 [WT-EventQueue-0] INFO s.billing.ui.Form - newheight is 783
2013-06-20 22:08:21,234 [WT-EventQueue-0] INFO s.billing.ui.Form - sizes now: panel: 543, scrollpane: 1086
我在那里添加了 setMinimumSize 以试图更具说服力,但它没有帮助。
任何想法都非常欢迎
编辑:添加了 SSCCE
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.ScrollPaneConstants;
import javax.swing.Timer;
import javax.swing.UIManager;
class Frame extends JFrame {
private JPanel panelLeft;
private JScrollPane scrollPane;
private JPanel movingPanel;
private JPanel upper;
private JPanel lower;
private boolean inited;
private JLabel labelUpper;
private JLabel labelLower;
private JButton scrollBtn;
private Frame.PanelSlideController panelSlideController;
private JButton resizeBtn;
private boolean lowerShowing = false;
public Frame() {
getContentPane().setLayout(new BorderLayout());
panelLeft = new JPanel();
panelLeft.setBackground(Color.CYAN);
panelLeft.setPreferredSize(new Dimension(300, 400));
getContentPane().add(panelLeft, BorderLayout.WEST);
labelUpper = new JLabel("upper");
panelLeft.add(labelUpper);
labelLower = new JLabel("lower");
panelLeft.add(labelLower);
resizeBtn = new JButton("resize");
resizeBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doResize();
}
});
panelLeft.add(resizeBtn);
scrollBtn = new JButton("Scroll");
scrollBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doScroll();
}
});
panelLeft.add(scrollBtn);
scrollPane = new JScrollPane();
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
movingPanel = new JPanel(new GridLayout(2, 1));
movingPanel.setOpaque(false);
movingPanel.setPreferredSize(new Dimension(300, 400));
upper = new JPanel();
upper.setBackground(Color.YELLOW);
movingPanel.add(upper);
lower = new JPanel();
lower.setBackground(Color.RED);
movingPanel.add(lower);
scrollPane.setViewportView(movingPanel);
getContentPane().add(scrollPane, BorderLayout.EAST);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
inited = true;
}
/**
* This is a manual step instead of overriding validate()
*/
protected void doResize() {
// Get the height we want
int referenceHeight = panelLeft.getSize().height;
// Update the height of the lower panel to equal this
Dimension size = lower.getSize();
size.height = referenceHeight;
lower.setPreferredSize(size);
lower.setMinimumSize(size);
// Update the height of the surrounding panel
size = scrollPane.getSize();
size.height = referenceHeight * 2;
movingPanel.setPreferredSize(size);
movingPanel.setMinimumSize(size);
if (panelSlideController != null) {
panelSlideController.redraw();
System.out.println("redrawing panel slide controller");
}
upper.invalidate();
lower.invalidate();
scrollPane.revalidate();
}
protected void doScroll() {
panelSlideController = new PanelSlideController(scrollPane, 20);
int scrollDirection = lowerShowing ? -1 : 1;
panelSlideController.scrollY(panelLeft.getHeight() * scrollDirection);
lowerShowing = !lowerShowing;
}
@Override
public void validate() {
super.validate();
System.out.println("Validating");
if (inited) {
labelUpper.setText("upper: " + upper.getSize().height);
labelLower.setText("lower: " + lower.getSize().height);
}
}
class PanelSlideController implements ActionListener {
private final JScrollPane scrollPane;
private final int speed;
private Timer timer;
private int endPos;
private boolean scrollingPositive;
public PanelSlideController(JScrollPane scrollPane, int speed) {
this.scrollPane = scrollPane;
this.speed = speed;
}
public void scrollY(int scrollDistance) {
endPos = scrollPane.getViewport().getViewPosition().y + scrollDistance;
scrollingPositive = scrollDistance > 0;
timer = new Timer(speed, this);
timer.start();
}
public void redraw() {
JViewport viewport = scrollPane.getViewport();
Point position = viewport.getViewPosition();
if (scrollingPositive) {
position.y = endPos;
}
else {
position.y = 0;
}
viewport.setViewPosition(position);
}
@Override
public void actionPerformed(ActionEvent e) {
JViewport viewport = scrollPane.getViewport();
Point position = viewport.getViewPosition();
int offset = scrollingPositive ? 10 : -10;
position.y += offset;
viewport.setViewPosition(position);
if ((scrollingPositive && position.y >= endPos)
|| (!scrollingPositive && (position.y <= endPos || position.y <= 0))) {
timer.stop();
}
}
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
}
对,所以上面是可以运行的。panelLeft(青色)用作参考高度。我将代码从 validate() 中移出,而是通过单击调整大小按钮来运行。
因此,您可以设置红色面板(下部)在单击滚动之前不可见,此时黄色向上滚动,而红色滚动进入视图。然后再朝相反的方向返回。为此,我需要黄色面板占据所有垂直高度,如果我可以使用布局管理器做到这一点,那就好。
我已经更新了原始片段和“图表”以反映此处使用的名称。
谢谢