使 JscrollPane 的视口大小取决于 Scrollpane 容器的大小和布局,而不是视口视图的大小?

问题描述 投票:0回答:1

根据有关如何正确调整滚动窗格大小的文档,表示滚动窗格的首选大小取决于视口视图的大小。

我遇到一个问题,其中有一个滚动窗格,其中有一个 JEditorPane。这里的默认行为(在我看来,如果 JEditorPane 应该是“滚动智能”,则没有任何意义)是滚动窗格扩展其大小,以便显示整个编辑器窗格。显然这是行不通的,因为我正在尝试布局我的组件以使其大小适合屏幕。

下图显示了我的设置。我正在使用 MigLayout。

The entire structure is inside the bottom half of a JSplitPane, and is thus vertically resizeable.
--------------JPanel inside JScrollPane (Horizantal scrolling only)-----------|
| |------JPanel 1-------| |--------JPanel 2-------| |--------Jpanel 3-------| |
| | |-----------------| | |     Same as first     | |   Same as first       | |
| | |---Header Panel--| | |                       | |                       | |
| | |-----------------| | |                       | |                       | | -->
| |                     | |                       | |                       | | --> 
| | |-VerticalScroll--| | |                       | |                       | | -->
| | |   JEditorPane   | | |                       | |                       | |
| | |   inside here   | | |                       | |                       | |
| | |-----------------| | |                       | |                       | |
| |---------------------| |-----------------------| |-----------------------| |
|-----------------------------------------------------------------------------|

您可能可以忽略外部组件。我只关注 JPanel 1,它的高度可以随着其祖先 JSplitPane 大小的调整而变化,以及 VerticalScroll 组件,它应该简单地适合它的内部。

简单地说:如何让垂直滚动窗格填充 JPanel 1/2..etc 内所有剩余的垂直空间?我希望标题保持在顶部,并且滚动窗格调整其视口的大小不是基于其视口视图大小,而是基于其父容器的大小。也就是说:无论 JScrollPane 内的组件如何,无论其大小或类型,我只是希望 JScrollPane 相对于其所在容器的布局来设置它及其视口大小。

我只关注滚动窗格的高度,而不是宽度。这是一个需要解决的单独问题,它可能会也可能不会成为我为此找到的解决方案的因素。

我尝试过各种方法,例如尝试计算组件高度和位置并从那里手动设置首选高度,但我总是遇到尺寸无限增长的问题,或者它不完全适合容器。

java swing
1个回答
0
投票

MigLayout 似乎不是最好的解决方案。也许 MigLayout 可以 做到这一点,但我怀疑它能否干净或轻松地做到这一点。

正如其他人所指出的,您可以简单地嵌套不同的布局。这是一种非常常见的做法,因为这就是布局的使用方式。

在每个面板中,您都有一个可调整大小、可滚动的组件:JEditorPane。这应该是 BorderLayout 的中心,因此当父面板大小调整时它可以调整大小。我猜标题不需要更改大小,因此它应该位于 BorderLayout 的 PAGE_START(即拉丁语言环境中的 NORTH)部分。

然后,将所有三个面板放入 GridLayout 中,以强制它们具有相同的大小。

我是这样做的:

import java.time.ZonedDateTime;

import java.io.IOException;

import java.net.URL;
import java.net.URI;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.GridLayout;
import java.awt.EventQueue;

import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JScrollPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JFrame;
import javax.swing.BorderFactory;

import javax.swing.text.AbstractDocument;

public class TripleEditorPanes {
    private JEditorPane editorPane1;
    private JEditorPane editorPane2;
    private JEditorPane editorPane3;

    private JComponent buildEditorPanel(String title,
                                        ZonedDateTime date,
                                        JEditorPane editor) {

        JLabel titleLabel = new JLabel("Name: " + title);
        JLabel dateLabel = new JLabel(String.format("Date: %tc", date));

        JComponent header = new JPanel(new GridLayout(0, 1, 0, 6));
        header.add(titleLabel);
        header.add(dateLabel);
        header.setBorder(BorderFactory.createEmptyBorder(6, 6, 12, 6));

        JScrollPane editorScrollPane = new JScrollPane(editor);

        JComponent editorPanel = new JPanel(new CardLayout());
        editorPanel.add(editorScrollPane, "editor");
        // Make editor's default size equivalent to 20 columns by 12 rows.
        editorPanel.add(new JScrollPane(new JTextArea(12, 20)), "sizer");

        JComponent panel = new JPanel(new BorderLayout());
        panel.add(header, BorderLayout.PAGE_START);
        panel.add(editorPanel, BorderLayout.CENTER);

        return panel;
    }

    private JEditorPane createEditorPane(URL url)
    throws IOException {
        JEditorPane editorPane = new JEditorPane();
        editorPane.setContentType("text/html");
        editorPane.setEditable(false);

        AbstractDocument doc = (AbstractDocument)
            editorPane.getEditorKit().createDefaultDocument();

        doc.setAsynchronousLoadPriority(0);
        editorPane.setDocument(doc);
        editorPane.setPage(url);

        return editorPane;
    }

    private JComponent buildEditorsPanel()
    throws IOException {
        URI page1 = URI.create(
            "https://docs.oracle.com/en/java/javase/21/docs/api/" +
            "java.base/java/lang/Void.html");
        URI page2 = URI.create(
            "https://docs.oracle.com/en/java/javase/21/docs/api/" +
            "java.base/java/lang/Runnable.html");
        URI page3 = URI.create(
            "https://docs.oracle.com/en/java/javase/21/docs/api/" +
            "java.base/java/lang/Iterable.html");

        editorPane1 = createEditorPane(page1.toURL());
        editorPane2 = createEditorPane(page2.toURL());
        editorPane3 = createEditorPane(page3.toURL());

        ZonedDateTime d1 = ZonedDateTime.now().minusMonths(6).minusDays(10);
        ZonedDateTime d2 = ZonedDateTime.now().minusMonths(3).minusDays(21);
        ZonedDateTime d3 = ZonedDateTime.now().minusMonths(1).minusDays(2);

        JComponent panel1 = buildEditorPanel("First", d1, editorPane1);
        JComponent panel2 = buildEditorPanel("Second", d2, editorPane2);
        JComponent panel3 = buildEditorPanel("Third", d3, editorPane3);

        JComponent editorsPanel = new JPanel(new GridLayout(1, 0, 12, 0));
        editorsPanel.add(panel1);
        editorsPanel.add(panel2);
        editorsPanel.add(panel3);

        return editorsPanel;
    }

    void show() {
        try {
            JComponent editors = buildEditorsPanel();

            JFrame frame = new JFrame("Editors");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(editors);
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> new TripleEditorPanes().show());
    }
}

您可能会注意到,我没有考虑您的水平滚动条目标。如果一个维度的滚动条位于一个组件上,而另一个维度的滚动条位于不同的组件上,则会导致用户界面笨拙,这可能会让开发人员满意,但对最终用户来说却是一种不愉快的体验。相反,我让每个 JEditorPane 的 JScrollPane 负责水平滚动;由于所有 JEditorPanes 均可调整大小,因此用户始终可以将它们放大以消除水平滚动的需要。

如果您确实觉得外部水平滚动条是必要的,则必须创建 JPanel 的子类,它实现 Scrollable,正如 MadProgrammer 指出的那样,因此它将始终使用包含 JScrollPane 的高度。

© www.soinside.com 2019 - 2024. All rights reserved.