不同层次结构层上的多个JScrollPane(Java):水平滚动条问题
我正在努力解决以下问题: 我在布局的不同位置有多个JScrollPane。当只使用一个JScrollPane时,一切都正常。不幸的是,第二个问题让我陷入了深深的麻烦之中 此外,还有以下要求: -必须使用JTextPane(需要richt文本格式) -没有第三方库(这会让生活更轻松,但目前还不是这样) (此处允许) -JTextPane仍应保持其当前的行为(说明:有两个JTextPane由一个JSplitter分隔。由于JScrollPane的计数不同,它们的行为不同。目标是所有情况下的行为相同(第一个)) 以下是我编写的代码:不同层次结构层上的多个JScrollPane(Java):水平滚动条问题,java,swing,user-interface,jscrollpane,jtextpane,Java,Swing,User Interface,Jscrollpane,Jtextpane,我正在努力解决以下问题: 我在布局的不同位置有多个JScrollPane。当只使用一个JScrollPane时,一切都正常。不幸的是,第二个问题让我陷入了深深的麻烦之中 此外,还有以下要求: -必须使用JTextPane(需要richt文本格式) -没有第三方库(这会让生活更轻松,但目前还不是这样) (此处允许) -JTextPane仍应保持其当前的行为(说明:有两个JTextPane由一个JSplitter分隔。由于JScrollPane的计数不同,它们的行为不同。目标是所有情况下的行为相同(
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的jscrollpane上获得相同的行为吗
编辑#1:
@rdonuk想出了一个有效的解决方案。我仍然更喜欢不依赖于使用任何集合(首选|最大值|最小值)
方法的解决方案。此外,我还提出了一个解决方案,该方案适用于我的具体情况,但可能不适用于其他LayoutManager。此外,我也不喜欢这种方法,我仍在寻找更好的解决方案
编辑#2:
调整了澄清要求
编辑#3:
添加了第三个解决方案(由@MadProgrammer提供)
解决方案1
解决方案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
编辑#4:
@MadProgrammer和@rdonuk提供的两种解决方案都能正常工作,可能比我的解决方案要好,但由于最终用户界面相当复杂,而且这两种解决方案要么需要大量工作,要么在特定环境下无法工作,我将坚持使用我的解决方案(解决方案2)。请尝试下面的代码。简而言之我刚刚修复了scrollPaneText2的大小,并在主面板中添加了一个调整大小的侦听器。因此,如果用户调整窗口大小,scrollPaneText2的大小将根据新的大小再次固定
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextPane;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BoxView;
import javax.swing.text.ComponentView;
import javax.swing.text.Element;
import javax.swing.text.IconView;
import javax.swing.text.LabelView;
import javax.swing.text.ParagraphView;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
public class ScrollExample extends JPanel {
JScrollPane secondScrollPane;
JScrollPane scrollPaneText2;
JPanel panel;
public ScrollExample() {
super(new BorderLayout());
addComponentListener(new ResizeListener());
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);
scrollPaneText2 = new JScrollPane(textPane2);
scrollPaneText2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPaneText2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
panel = new JPanel(new BorderLayout());
panel.add(scrollPaneText2, BorderLayout.WEST);
panel.add(new JButton("Example"), BorderLayout.NORTH);
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 void adjustComponents() {
panel.remove(scrollPaneText2);
Dimension dimension = new Dimension();
int nScrollWidth = secondScrollPane.getVerticalScrollBar().getWidth();
dimension.setSize(secondScrollPane.getVisibleRect().getWidth()-nScrollWidth, scrollPaneText2.getHeight());
scrollPaneText2.setPreferredSize(dimension);
scrollPaneText2.setMaximumSize(dimension);
panel.add(scrollPaneText2);
repaint();
}
public static void main(String[] args) {
final ScrollExample example = new ScrollExample();
final 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);
}
}
public class ResizeListener extends ComponentAdapter {
@Override
public void componentResized(ComponentEvent e) {
ScrollExample scrollExample = (ScrollExample) e.getSource();
scrollExample.adjustComponents();
}
}
}
制作一个
JPanel
,它实现了Scrollable
,并为getPreferredScrollableViewportSize
见我最初的帖子。示例代码中有3种解决方案。您的GUI应该是什么样子的?请查看可滚动的
界面,该界面可用于向JScrollPane
提供有关其“首选”视口大小的提示be@GilbertLeBlanc:上面的示例是为了简单而进行的抽象。最后,用户界面将有许多组件,这些组件不适合屏幕(限于屏幕宽度,在最外层的JScrollPane上不会有水平滚动条)。因此,添加了外部JScrollPane(JSplitter的底部)。内容将是动态的,取决于用户交互。但是JTextPane(包括JScrollPane)的行为应该与JSplitPane的上半部分相同。@GilbertLeBlanc:part#2:JTextPane的内容也是动态的。所有的可能都很合适,也可能是一团糟。因此,它应该包含滚动条,因为组件的高度稍后会受到限制(宽度应该适合屏幕,这是当前的问题)。@MadProgrammer:我已经查看了Scrollable,并覆盖了getScrollableTracksViewPortWidth()
以返回false
,如其他问题所述。这根本不起作用(破坏了组件的大小调整)。由于它是组件的一部分,而且JTextPane的内容也是动态的,所以我不知道如何提供“首选”视口大小。有什么建议吗?@rdonuk:这个解决方案似乎有效,但正如@MadProgrammer提到的,如果可能的话,我更喜欢不依赖set(首选的|最大|最小)大小的解决方案。我会投票支持你的答案,但不幸的是,由于我的信誉点(新帐户),这是不可能的。如果没有设置(首选|最大|最小)大小,我无法找到解决方案
我会将此标记为解决问题的答案。好的。如果您找到其他解决方案,请在此处发布。@rdonuk:将您的方法添加到可能的解决方案中。此外,我还添加了另一个适用于我的案例的解决方案。但我还远远没有满意。@CodeMonkey请检查我的第二个解决方案。它有效,而且肯定是一个解决方案。尽管如此,我还是觉得有点“不对”。说明:(TextWrap)JTextPane是自定义组件的一部分。该自定义组件不应该考虑它的布局(JTextPane是其中的一部分)而不是必须在使用自定义组件的任何地方添加的受限窗格吗?在解决方案#2中,我重写了getPreferredSize方法(我的(子)自定义组件的),这导致了相同的结果。作为swing新手,如果您能解释一下为什么您的解决方案是可行的,我将不胜感激。您之前发布的链接涉及setXSize
方法,其中一些注释建议自定义组件可能会覆盖getXSi
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Rectangle;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextPane;
import javax.swing.Scrollable;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BoxView;
import javax.swing.text.ComponentView;
import javax.swing.text.Element;
import javax.swing.text.IconView;
import javax.swing.text.LabelView;
import javax.swing.text.ParagraphView;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
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 RestrictedPanel();
panel.setLayout(new BorderLayout());
panel.add(scrollPaneText2, BorderLayout.CENTER);
panel.add(new JButton("Example"), BorderLayout.NORTH);
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPaneText1, panel);
splitPane.setDividerLocation(100);
add(splitPane);
textPane1.setText("ThisIsAVeryLongStringWhichRepeatsItselfThisIsAVeryLongStringWhichRepeatsItself ThisIsAVeryLongStringWhichRepeatsItself");
textPane2.setText("ThisIsAVeryLongStringWhichRepeatsItselfThisIsAVeryLongStringWhichRepeatsItself ThisIsAVeryLongStringWhichRepeatsItself");
}
public class RestrictedPanel extends JPanel implements Scrollable {
@Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(600, 200);
}
@Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 128;
}
@Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 128;
}
@Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public boolean getScrollableTracksViewportHeight() {
return true;
}
}
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);
}
}
}