java - 出现垂直滚动条时 JScrollPane 不够宽

标签 java swing jscrollpane

我在同一窗口中有两个 JScrollPanes。左边的那个足够大,可以显示所包含面板的内容。右边的一个不够大,无法显示其内容,因此需要创建一个垂直滚动条。

JScrollPane Issue

但是如你所见,问题是当垂直滚动条出现时,滚动条出现在JScrollPane的内部。它覆盖了内部包含的内容,因此需要一个水平滚动条来显示所有内容。我想修复它。

我知道我可以一直打开垂直滚动条,但出于美观原因,我只希望它在必要时出现,而不必出现水平滚动条。

编辑:我的启动代码尽可能简单:

JScrollPane groupPanelScroller = new JScrollPane(groupPanel);
this.add(groupPanelScroller, "align center");

我正在使用 MigLayout (MigLayout.com),但无论我使用什么布局管理器,这个问题似乎都会出现。此外,如果我缩小窗口使得左侧面板不再大到足以显示所有内容,则会发生与右侧面板相同的行为。

最佳答案

第一个:never-ever tweak the sizing hints在组件级别。尤其是当您确实拥有强大的 LayoutManager(例如支持在管理器级别进行调整的 MigLayout)时。

在代码中,调整任意大小的 pref 大小:

// calculate some width to add to pref, f.i. to take the scrollbar width into account
final JScrollPane pane = new JScrollPane(comp);
int prefBarWidth = pane.getVerticalScrollBar().getPreferredSize().width;
// **do not**  
comp.setPreferredSize(new Dimension(comp.getPreferredSize().width + prefBarWidth, ...);
// **do**
String pref = "(pref+" + prefBarWidth + "px)"; 
content.add(pane, "width " + pref);

就是说:基本上,您在 ScrollPaneLayout 中遇到了一个(有争议的)错误。虽然看起来像是将滚动条宽度考虑在内,但实际上并非在所有情况下都如此。来自 preferredLayoutSize 的相关片段

// filling the sizes used for calculating the pref
Dimension extentSize = null;
Dimension viewSize = null;
Component view = null;

if (viewport != null) {
    extentSize = viewport.getPreferredSize();
    view = viewport.getView();
    if (view != null) {
        viewSize = view.getPreferredSize();
    } else {
        viewSize = new Dimension(0, 0);
    }
}

....

// the part trying to take the scrollbar width into account

if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
    if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
        prefWidth += vsb.getPreferredSize().width;
    }
    else if ((viewSize != null) && (extentSize != null)) {
        boolean canScroll = true;
        if (view instanceof Scrollable) {
            canScroll = !((Scrollable)view).getScrollableTracksViewportHeight();
        }
        if (canScroll && 
            // following condition is the **culprit** 
            (viewSize.height > extentSize.height)) {
            prefWidth += vsb.getPreferredSize().width;
        }
    }
}

这是罪魁祸首,因为

  • 它正在将 View 偏好与视口(viewport)偏好进行比较
  • 大多数时候他们是一样的

结果就是您所看到的:滚动条与 View 重叠(在切断一些宽度的意义上)。

一个变通方法是自定义 ScrollPaneLayout,如果 View 的高度小于实际视口(viewport)高度,它会添加滚动条宽度,这是一个粗略的示例(注意:不是生产质量) 一起玩

public static class MyScrollPaneLayout extends ScrollPaneLayout {

    @Override
    public Dimension preferredLayoutSize(Container parent) {
        Dimension dim =  super.preferredLayoutSize(parent);
        JScrollPane pane = (JScrollPane) parent;
        Component comp = pane.getViewport().getView();
        Dimension viewPref = comp.getPreferredSize();
        Dimension port = pane.getViewport().getExtentSize();
        // **Edit 2** changed condition to <= to prevent jumping
        if (port.height < viewPref.height) {
            dim.width += pane.getVerticalScrollBar().getPreferredSize().width;
        }
        return dim;
    }

}

编辑

嗯...看到跳跃(在显示与不显示垂直滚动条之间,如评论中所述):当我用另一个 scrollPane 替换我的示例中的文本字段时,然后调整“接近”其首选项宽度的大小显示问题。所以 hack 不够好,可能是询问视口(viewport)范围的时间不正确(在布局过程的中间)。目前不知道,如何做得更好。

编辑2

试探性跟踪:当逐个像素地改变宽度时,感觉就像是一次性错误。从 < 更改条件至 <=似乎修复了跳跃 - 以总是添加滚动条宽度为代价。所以总的来说,这导致第一步具有更广泛的尾随插图;-) 同时相信 scollLlayout 的整个逻辑需要改进......

总结您的选择:

  • 调整 (MigLayout) componentConstraint 中的 pref 宽度。这是最简单的,缺点是在滚动条不显示的情况下会有额外的尾随空白
  • 修复 scrollPaneLayout。需要一些努力和测试(请参阅核心 ScrollPaneLayout 的代码需要做什么),优点是没有滚动条的一致填充
  • 不是一个选项在组件上手动设置 pref 宽度

下面是代码示例:

// adjust the pref width in the component constraint
MigLayout layout = new MigLayout("wrap 2", "[][]");
final JComponent comp = new JPanel(layout);
for (int i = 0; i < 10; i++) {
    comp.add(new JLabel("some item: "));
    comp.add(new JTextField(i + 5));
}

MigLayout outer = new MigLayout("wrap 2", 
        "[][grow, fill]");
JComponent content = new JPanel(outer);
final JScrollPane pane = new JScrollPane(comp);
int prefBarWidth = pane.getVerticalScrollBar().getPreferredSize().width;
String pref = "(pref+" + prefBarWidth + "px)";
content.add(pane, "width " + pref);
content.add(new JTextField("some dummy") );
Action action = new AbstractAction("add row") {

    @Override
    public void actionPerformed(ActionEvent e) {
        int count = (comp.getComponentCount() +1)/ 2;
        comp.add(new JLabel("some Item: "));
        comp.add(new JTextField(count + 5));
        pane.getParent().revalidate();
    }
};
frame.add(new JButton(action), BorderLayout.SOUTH);
frame.add(content);
frame.pack();
frame.setSize(frame.getWidth()*2, frame.getHeight());
frame.setVisible(true);

// use a custom ScrollPaneLayout
MigLayout layout = new MigLayout("wrap 2", "[][]");
final JComponent comp = new JPanel(layout);
for (int i = 0; i < 10; i++) {
    comp.add(new JLabel("some item: "));
    comp.add(new JTextField(i + 5));
}

MigLayout outer = new MigLayout("wrap 2", 
        "[][grow, fill]");
JComponent content = new JPanel(outer);
final JScrollPane pane = new JScrollPane(comp);
pane.setLayout(new MyScrollPaneLayout());
content.add(pane);
content.add(new JTextField("some dummy") );
Action action = new AbstractAction("add row") {

    @Override
    public void actionPerformed(ActionEvent e) {
        int count = (comp.getComponentCount() +1)/ 2;
        comp.add(new JLabel("some Item: "));
        comp.add(new JTextField(count + 5));
        pane.getParent().revalidate();
    }
};
frame.add(new JButton(action), BorderLayout.SOUTH);
frame.add(content);
frame.pack();
frame.setSize(frame.getWidth()*2, frame.getHeight());
frame.setVisible(true);

关于java - 出现垂直滚动条时 JScrollPane 不够宽,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11587292/

相关文章:

java - 显示可滚动的 JTable

java - Java Swing 窗口应用程序有多安全

java - 如何使用 Java Swing 将一堆 JPanel 添加到我的 JFrame 中?

Java 对 Javabean 嵌套属性进行 null 检查

java - filesystem.normalize 背后的代码

Java Swing : perform action when video is finished

java - JPanel 中行的动态宽度和固定高度

java - 不使用 setModel() 使 Jtable Noneditable

java - 使用 Selenium WebDriver Java - css 按钮上传照片

java - 滚动条没有被添加到面板