根据有关如何正确调整滚动窗格大小的文档,表示滚动窗格的首选大小取决于视口视图的大小。
我遇到一个问题,其中有一个滚动窗格,其中有一个 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 相对于其所在容器的布局来设置它及其视口大小。
我只关注滚动窗格的高度,而不是宽度。这是一个需要解决的单独问题,它可能会也可能不会成为我为此找到的解决方案的因素。
我尝试过各种方法,例如尝试计算组件高度和位置并从那里手动设置首选高度,但我总是遇到尺寸无限增长的问题,或者它不完全适合容器。
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 的高度。