简体   繁体   English

不同层次结构层上的多个JScrollPane(Java):水平滚动条问题

[英]Multiple JScrollPane (Java) on different hierarchy layer: horizontal scroll bar issue

I'm struggling with the following issue: 我正在努力解决以下问题:

I have multiple JScrollPanes at different positions in the layout. 我在布局中的不同位置有多个JScrollPanes。 Everything works when while using only one JScrollPane. 只使用一个JScrollPane时,一切正常。 Unfortunately the second one is getting me into deep troubles. 不幸的是,第二个让我陷入了深深的麻烦。

Furthermore, there are the following requirements: 此外,还有以下要求:

-A JTextPane has to be used (richt text formating is required) - 必须使用JTextPane(需要格式化文本格式化)

-No third party libraries (would make life easier, but it isn't currently allowed here) - 没有第三方图书馆(会让生活更轻松,但目前不允许)

-The JTextPane should still behave as it currently does (Explanation: There are 2 JTextPanes separeted by a JSplitter. They behave differntly due to a different count of JScrollPanes. The goal is that the behave in all situations the same (first one)) -JTextPane应该仍然像现在一样运行(解释:JSplitter有2个JTextPanes。由于JScrollPanes的计数不同,它们的行为也不同。目标是在所有情况下表现相同(第一个))

Below is the code I've wrote: 以下是我写的代码:

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);
        }
    }
}

Explanation: 说明:

-The inner classes are needed for the right behaviour of the JTextPane (it will break "long" words instead of messing up the UI). - JTextPane的正确行为需要内部类(它将打破“长”字而不是弄乱UI)。

-The upper portion of the JSplitPane shows how the JTextPane behaves correctly (text wrapping and adding scrollbar (vertical) when needed) - JSplitPane的上半部分显示JTextPane的行为方式(文本环绕并在需要时添加滚动条(垂直))

-The lower portion adds a random button and the JTextPane (incl. the JScrollPane). - 下半部分添加一个随机按钮和JTextPane(包括JScrollPane)。 The button is just for illustration as in this region many other components should take part of the UI. 该按钮仅用于说明,因为在该区域中许多其他组件应该参与UI。

The issue now is in the lower part of the JSpitPane. 现在问题出在JSpitPane的下半部分。 The JScrollPane of the JTextPane doesn't behave the same way as it does when there isn't a second JScrollPane. JTextPane的JScrollPane的行为与没有第二个JScrollPane时的行为方式不同。

Does anyone know or could give me a hint how to get the same behaviour on both JTextPane's JScrollPanes? 有没有人知道或者可以给我一个提示如何在JTextPane的JScrollPanes上获得相同的行为?

EDIT #1: 编辑#1:

@rdonuk came up with a solution that works. @rdonuk想出了一个有效的解决方案。 I'd still prefer a solution without relying on using any of the set(Preferred|Maximum|Minimum) methods. 我仍然更喜欢不使用任何set(Preferred|Maximum|Minimum)方法的解决方案。 Furthermore, i came up with a solution that works in my specific case, but might not work with other LayoutManager. 此外,我提出了一个适用于我的特定情况的解决方案,但可能无法与其他LayoutManager一起使用。 Furthermore, I don't like that approach either and I'm still looking for a better solution. 此外,我也不喜欢这种方法,我仍然在寻找更好的解决方案。

EDIT #2: 编辑#2:

Adjusted requirements for clarification. 调整后的澄清要求。

EDIT #3: 编辑#3:

Third solution added (provided by @MadProgrammer). 添加了第三个解决方案(由@MadProgrammer提供)。

Solution 1 解决方案1

See below for @rdonuk's solution 请参阅下面的@ rdonuk解决方案

Solution 2 解决方案2

I simply overwrote the getPreferredSize methods of the JScrollPanes: 我只是覆盖了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);
        }
    }
}

Solution 3 解决方案3

See below for @MadProgrammer's solution 请参阅下面的@ MadProgrammer解决方案

EDIT #4: 编辑#4:

Both of the solutions given by @MadProgrammer and @rdonuk work and might be better than mine in general but as the final UI is quite complex and both of the solutions either require a lot of work or won't work in that specific environment I'll stick to my solution (Solution #2). @MadProgrammer和@rdonuk提供的两种解决方案都可以比我的更好,但是由于最终的UI非常复杂,并且这两种解决方案要么需要大量的工作,要么在特定的环境中无法工作。坚持我的解决方案(解决方案#2)。

Make a JPanel which implements Scrollable and provide a solution for getPreferredScrollableViewportSize 创建一个实现ScrollableJPanel ,并为getPreferredScrollableViewportSize提供解决方案

RestrcitedPane

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);
        }
    }
}

Please try code below. 请尝试下面的代码。 In a nutshell; 简而言之; I just fixed the scrollPaneText2 size and added a resize listener to your main panel. 我刚刚修复了scrollPaneText2大小,并在主面板上添加了一个调整大小的侦听器。 So if user resize the window, size of the scrollPaneText2 being fixed again according to the new size. 因此,如果用户调整窗口大小,则会根据新大小再次修复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();
        }
    }
}

See my initial post. 看我的初始帖子。 There are 3 solutions that would work in the example code. 有3个解决方案可以在示例代码中使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM