我在我的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,这意味着movePanel的一半显示,而上端位于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()中移出,而是通过单击调整大小按钮来运行。
所以你可以看到红色面板(下方),如果不可见,直到点击滚动,此时黄色向上滚动,而红色滚动到视图中。然后再向相反的方向返回。要做到这一点,我需要黄色面板占据所有垂直高度,如果我可以使用布局管理器,那么yay。
我已更新原始代码段和“图表”以反映此处使用的名称。
由于
答案 0 :(得分:0)
好的,由于提示使用LayoutManager,我在那个方向上做了一些挖掘。事实证明,JScrollPanes使用JViewports,后者又使用了LayoutManager的ViewportLayout实现。它们可以控制其委派的“视图”组件。从下面的修改后的代码中可以看出,我现在重写了layoutContainer方法,使我的'movingPanel'相对于视口的高度加倍,这比以前和XP中最大化时效果明显更好。
由于
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.LayoutManager;
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;
import javax.swing.ViewportLayout;
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);
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);
// ------------------------------
// This is the key bit
// ------------------------------
JViewport viewport = new JViewport() {
@Override
protected LayoutManager createLayoutManager() {
return new ViewportLayout() {
@Override
public void layoutContainer(Container parent) {
JViewport vp = (JViewport) parent;
Component view = vp.getView();
Dimension viewPrefSize = view.getPreferredSize();
Dimension vpSize = vp.getSize();
Dimension viewSize = new Dimension(viewPrefSize);
viewSize.width = vpSize.width;
viewSize.height = vpSize.height * 2;
vp.setViewSize(viewSize);
}
};
}
};
scrollPane.setViewport(viewport);
viewport.setView(movingPanel);
// ------------------------------
// End of key bit
// ------------------------------
getContentPane().add(scrollPane, BorderLayout.EAST);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
inited = true;
}
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);
}
});
}
}
注意:在删除之前我打开了一个后续问题,并在此处包含标题用于搜索引擎优化目的:如何使用Swing布局管理器来包含框架内的对象?