【发布时间】:2016-03-22 18:10:05
【问题描述】:
我正在努力解决以下问题:
我在布局的不同位置有多个 JScrollPanes。仅使用一个 JScrollPane 时一切正常。不幸的是,第二个让我陷入了深深的麻烦。
此外,还有以下要求:
-必须使用 JTextPane(需要富文本格式)
-没有第三方库(会让生活更轻松,但目前还不是 此处允许)
-JTextPane 的行为仍应与当前一样(解释:有 2 个 JTextPanes 由 JSplitter 分隔。由于 JScrollPanes 的数量不同,它们的行为不同。目标是在所有情况下的行为都相同(第一个))
下面是我写的代码:
public class ScrollExample extends JPanel {
public ScrollExample() {
super(new BorderLayout());
JTextPane textPane1 = new JTextPane();
textPane1.setEditorKit(new WrapEditorKit());
JTextPane textPane2 = new JTextPane();
textPane2.setEditorKit(new WrapEditorKit());
JScrollPane scrollPaneText1 = new JScrollPane(textPane1);
scrollPaneText1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPaneText1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
JScrollPane scrollPaneText2 = new JScrollPane(textPane2);
scrollPaneText2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPaneText2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
JPanel panel = new JPanel(new BorderLayout());
panel.add(scrollPaneText2, BorderLayout.CENTER);
panel.add(new JButton("Example"), BorderLayout.NORTH);
JScrollPane secondScrollPane = new JScrollPane(panel);
secondScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
secondScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPaneText1, secondScrollPane);
splitPane.setDividerLocation(100);
add(splitPane);
textPane1.setText("ThisIsAVeryLongStringWhichRepeatsItselfThisIsAVeryLongStringWhichRepeatsItself ThisIsAVeryLongStringWhichRepeatsItself");
textPane2.setText("ThisIsAVeryLongStringWhichRepeatsItselfThisIsAVeryLongStringWhichRepeatsItself ThisIsAVeryLongStringWhichRepeatsItself");
}
public static void main(String[] args) {
ScrollExample example = new ScrollExample();
JFrame frame = new JFrame("Example");
frame.setLayout(new BorderLayout());
frame.add(example, BorderLayout.CENTER);
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
frame.setBounds(100, 50, 600, 400);
frame.setVisible(true);
}
});
}
public class WrapLabelView extends LabelView {
public WrapLabelView(Element elem) {
super(elem);
}
@Override
public float getMinimumSpan(int axis) {
switch(axis) {
case View.X_AXIS:
return 0;
case View.Y_AXIS:
return super.getMinimumSpan(axis);
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
}
public class WrapEditorKit extends StyledEditorKit {
protected ViewFactory _factory = new WrapColumnFactory();
@Override
public ViewFactory getViewFactory() {
return _factory;
}
}
public class WrapColumnFactory implements ViewFactory {
@Override
public View create(Element elem) {
switch(elem.getName()) {
case AbstractDocument.ContentElementName:
return new WrapLabelView(elem);
case AbstractDocument.ParagraphElementName:
return new ParagraphView(elem);
case AbstractDocument.SectionElementName:
return new BoxView(elem, View.Y_AXIS);
case StyleConstants.ComponentElementName:
return new ComponentView(elem);
case StyleConstants.IconElementName:
return new IconView(elem);
}
return new LabelView(elem);
}
}
}
解释:
-JTextPane 的正确行为需要内部类(它会破坏“长”字而不是弄乱 UI)。
-JSplitPane 的上半部分显示了 JTextPane 如何正确运行(文本换行并在需要时添加滚动条(垂直))
-下部添加了一个随机按钮和 JTextPane(包括 JScrollPane)。该按钮仅用于说明,因为在该区域中,许多其他组件应成为 UI 的一部分。
现在问题出在 JSpitPane 的下部。 JTextPane 的 JScrollPane 的行为方式与没有第二个 JScrollPane 时的行为方式不同。
有谁知道或可以给我一个提示如何在两个 JTextPane 的 JScrollPanes 上获得相同的行为?
编辑#1:
@rdonuk 提出了一个可行的解决方案。我仍然更喜欢不依赖使用任何set(Preferred|Maximum|Minimum) 方法的解决方案。此外,我想出了一个适用于我的特定情况的解决方案,但可能不适用于其他 LayoutManager。此外,我也不喜欢这种方法,我仍在寻找更好的解决方案。
编辑 #2:
调整后的澄清要求。
编辑#3:
添加了第三个解决方案(由@MadProgrammer 提供)。
解决方案 1
See below for @rdonuk's solution
解决方案 2
我只是重写了 JScrollPanes 的 getPreferredSize 方法:
public class ScrollExample extends JPanel {
public ScrollExample() {
super(new BorderLayout());
JTextPane textPane1 = new JTextPane();
textPane1.setEditorKit(new WrapEditorKit());
JTextPane textPane2 = new JTextPane();
textPane2.setEditorKit(new WrapEditorKit());
JScrollPane scrollPaneText1 = new JScrollPane(textPane1) {
@Override
public Dimension getPreferredSize() {
return new Dimension(1,1);
}
};
scrollPaneText1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPaneText1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
JScrollPane scrollPaneText2 = new JScrollPane(textPane2) {
@Override
public Dimension getPreferredSize() {
return new Dimension(1,1);
}
};
scrollPaneText2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPaneText2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
JPanel panel = new JPanel(new BorderLayout());
panel.add(scrollPaneText2, BorderLayout.CENTER);
panel.add(new JButton("Example"), BorderLayout.NORTH);
JScrollPane secondScrollPane = new JScrollPane(panel);
secondScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
secondScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPaneText1, secondScrollPane);
splitPane.setDividerLocation(100);
add(splitPane);
textPane1.setText("ThisIsAVeryLongStringWhichRepeatsItselfThisIsAVeryLongStringWhichRepeatsItself ThisIsAVeryLongStringWhichRepeatsItself");
textPane2.setText("ThisIsAVeryLongStringWhichRepeatsItselfThisIsAVeryLongStringWhichRepeatsItself ThisIsAVeryLongStringWhichRepeatsItself");
}
public static void main(String[] args) {
ScrollExample example = new ScrollExample();
JFrame frame = new JFrame("Example");
frame.setLayout(new BorderLayout());
frame.add(example, BorderLayout.CENTER);
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
frame.setBounds(100, 50, 600, 400);
frame.setVisible(true);
}
});
}
public class WrapLabelView extends LabelView {
public WrapLabelView(Element elem) {
super(elem);
}
@Override
public float getMinimumSpan(int axis) {
switch(axis) {
case View.X_AXIS:
return 0;
case View.Y_AXIS:
return super.getMinimumSpan(axis);
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
}
public class WrapEditorKit extends StyledEditorKit {
protected ViewFactory _factory = new WrapColumnFactory();
@Override
public ViewFactory getViewFactory() {
return _factory;
}
}
public class WrapColumnFactory implements ViewFactory {
@Override
public View create(Element elem) {
switch(elem.getName()) {
case AbstractDocument.ContentElementName:
return new WrapLabelView(elem);
case AbstractDocument.ParagraphElementName:
return new ParagraphView(elem);
case AbstractDocument.SectionElementName:
return new BoxView(elem, View.Y_AXIS);
case StyleConstants.ComponentElementName:
return new ComponentView(elem);
case StyleConstants.IconElementName:
return new IconView(elem);
}
return new LabelView(elem);
}
}
}
解决方案 3
See below for @MadProgrammer's solution
编辑#4:
@MadProgrammer 和 @rdonuk 提供的两种解决方案都有效,总体上可能比我的要好,但由于最终的 UI 相当复杂,而且这两种解决方案要么需要大量工作,要么在特定情况下不起作用我会坚持我的解决方案(解决方案 #2)。
【问题讨论】:
-
你的 GUI 应该是什么样子的?
-
看看
Scrollable接口,它可以用来向JScrollPane提供关于它的“首选”视口大小应该是什么的提示 -
@GilbertLeBlanc :上面的例子是一个简单的抽象。最后,用户界面会有很多组件,不适合屏幕(受限于屏幕宽度,最外面的 JScrollPane 上不会有水平滚动条)。因此,添加了外部 JScrollPane(JSplitter 的底部)。内容将是动态的并且取决于用户交互。但是 JTextPane(包括 JScrollPane)的行为应该与 JSplitPane 的上部一样。
-
@GilbertLeBlanc:第 2 部分:JTextPane 的内容也是动态的。一切都可能非常合适,或者可能一团糟。因此,它应该包含滚动条,因为稍后会限制组件的高度(并且宽度应该适合屏幕,这是目前的问题)。
-
@MadProgrammer :正如其他问题中所建议的那样,我已经查看了 Scrollable 并覆盖了
getScrollableTracksViewPortWidth()以返回false。那根本不起作用(破坏了组件的大小调整)。因为它是组件的一部分,并且 JTextPane 的内容也是动态的,所以我不知道如何提供“首选”视口大小。有什么建议吗?
标签: java swing user-interface jscrollpane jtextpane