Swing JScrollPane - 如何将垂直滚动条设置为左侧?

发布于 2024-11-29 17:21:12 字数 2499 浏览 0 评论 0原文

看标题。我尝试将组件方向更改为 RIGHT_TO_LEFT,但这会产生意想不到的副作用 - 对于具有指定首选大小的组件,它的行为会很奇怪。

(JDK 1.6.0_23,Eclipse VE)

编辑

这是这样的示例:

我们有 JFrame 及其上的 jMainScrollPane。在jMainScrollPane内,我们放置一个jMainPanel。现在将jMainPanel的首选大小设置为比jMainScrollPane窄。 jMainPanel 仍将占用 jMainScrollPane 上的所有空间。现在将jMainScrollPane的方向更改为RIGHT_TO_LEFT并看看会发生什么。

示例代码(更改 jMainScrollPane 的方向以查看差异):

import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Dimension;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class TestFrame extends JFrame {
    private JPanel      jContentPane    = null;
    private JScrollPane jMainScrollPane = null;
    private JPanel      jMainPanel      = null;
    
    public TestFrame(){
        super();
        initialize();
    }
    
    private void initialize(){
        this.setSize(480, 339);
        this.setContentPane(getJContentPane());
        this.setTitle("JFrame");
    }
    
    private JPanel getJContentPane(){
        if (jContentPane == null) {
            jContentPane = new JPanel();
            jContentPane.setLayout(new BorderLayout());
            jContentPane.add(getJMainScrollPane(), BorderLayout.CENTER);
        }
        return jContentPane;
    }
    
    private JScrollPane getJMainScrollPane(){
        if (jMainScrollPane == null) {
            jMainScrollPane = new JScrollPane();
            jMainScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            jMainScrollPane.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
            jMainScrollPane.setViewportView(getJMainPanel());
        }
        return jMainScrollPane;
    }
    
    private JPanel getJMainPanel(){
        if (jMainPanel == null) {
            jMainPanel = new JPanel();
            jMainPanel.setLayout(new BorderLayout());
            jMainPanel.setPreferredSize(new Dimension(30, 30));
        }
        return jMainPanel;
    }
}

EDIT2

由于这种情况的奇怪性质,我已向 Oracle 提交了一个错误。他们向我邮寄了错误链接

https://bugs.java.com/bugdatabase/view_bug? bug_id=7038455

目前还没有这样的错误 - 我想它正在被检查。

但问题仍然存在——是否有解决方法或其他方法?

See the title. I've tried to change component orientation to RIGHT_TO_LEFT, but that had a unexpected side effect - it behaves strangely with components with specified preferred size.

(JDK 1.6.0_23, Eclipse VE)

EDIT

Here is the example of this:

We have JFrame with jMainScrollPane on it. Inside jMainScrollPane we place a jMainPanel. Now set jMainPanel's preferred size to be narrower than jMainScrollPane's. jMainPanel will still take all the space on jMainScrollPane. Now changejMainScrollPane's orientation to RIGHT_TO_LEFT and see what happen.

Sample code (change jMainScrollPane's orientation to see the difference):

import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Dimension;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class TestFrame extends JFrame {
    private JPanel      jContentPane    = null;
    private JScrollPane jMainScrollPane = null;
    private JPanel      jMainPanel      = null;
    
    public TestFrame(){
        super();
        initialize();
    }
    
    private void initialize(){
        this.setSize(480, 339);
        this.setContentPane(getJContentPane());
        this.setTitle("JFrame");
    }
    
    private JPanel getJContentPane(){
        if (jContentPane == null) {
            jContentPane = new JPanel();
            jContentPane.setLayout(new BorderLayout());
            jContentPane.add(getJMainScrollPane(), BorderLayout.CENTER);
        }
        return jContentPane;
    }
    
    private JScrollPane getJMainScrollPane(){
        if (jMainScrollPane == null) {
            jMainScrollPane = new JScrollPane();
            jMainScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            jMainScrollPane.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
            jMainScrollPane.setViewportView(getJMainPanel());
        }
        return jMainScrollPane;
    }
    
    private JPanel getJMainPanel(){
        if (jMainPanel == null) {
            jMainPanel = new JPanel();
            jMainPanel.setLayout(new BorderLayout());
            jMainPanel.setPreferredSize(new Dimension(30, 30));
        }
        return jMainPanel;
    }
}

EDIT2

Due to the strange nature of this, I've submitted a bug to Oracle. They mailed me a bug link

https://bugs.java.com/bugdatabase/view_bug?bug_id=7038455

At the moment there is no such bug yet - I suppose, it's being checked.

But still, question is open - is there a workaround or another way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

尸血腥色 2024-12-06 17:21:12

同意OP的观点:这是错误。 RToL方向的设置会触发viewPosition的计算。这发生在布局完成之前。在这种情况下,JViewport 返回视图的首选大小而不是其实际大小(当时为零)。在内心深处,BasicScrollPaneUI.Handler 不知道那个伪造的大小,并根据不正确的数据进行计算。

下面是一些可以使用的代码(OP 原始版本进一步精简,添加了彩色边框以查看位置):

public class COInScrollPane extends JFrame {

    public COInScrollPane(){
        super();
        initialize();
    }

    private void initialize(){
        this.setTitle("JFrame");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JScrollPane jMainScrollPane = getJMainScrollPane();
        this.add(jMainScrollPane);
        this.setSize(480, 339);
        Action action = new AbstractAction("Toggle CO") {

            @Override
            public void actionPerformed(ActionEvent e) {
                ComponentOrientation co = jMainScrollPane.getComponentOrientation().isLeftToRight() ?
                        ComponentOrientation.RIGHT_TO_LEFT : ComponentOrientation.LEFT_TO_RIGHT;
                jMainScrollPane.applyComponentOrientation(co);
                jMainScrollPane.revalidate();
                jMainScrollPane.repaint();
            }
        };
        add(new JButton(action), BorderLayout.SOUTH);
    }

    private JScrollPane getJMainScrollPane() {
        JScrollPane jMainScrollPane = new JScrollPane(getJMainPanel());
        jMainScrollPane.setViewportBorder(BorderFactory
                .createLineBorder(Color.GREEN));
        jMainScrollPane
                .applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        return jMainScrollPane;
    }

    private JPanel getJMainPanel() {
        JPanel jMainPanel = new JPanel();
        jMainPanel.add(new JButton("just a button"));
        jMainPanel.setBorder(BorderFactory.createLineBorder(Color.RED));
        return jMainPanel;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new COInScrollPane().setVisible(true);

            }
        });
    }
}

初始布局完全错误(如 OP 所报告),用按钮切换 CO 两次 - 布局看起来好的。

一个快速而肮脏的黑客可能是在生产环境中这样做:在初始布局后,强制 CO 进入 LToR 以允许正确的坐标,然后返回到 RToL。

Agree with the OP: it's bug. The setting of RToL orientation triggers the calculation of the viewPosition. This happens before the layout is done. In that case, the JViewport returns the view's preferredSize instead of its actual size (which at that time is zero). Deep in the bowels, the BasicScrollPaneUI.Handler doesn't know about that faked size and bases its calculation on incorrect data.

Here's some code to play with (the OPs original stripped down a bit further, added colored borders to see where is what):

public class COInScrollPane extends JFrame {

    public COInScrollPane(){
        super();
        initialize();
    }

    private void initialize(){
        this.setTitle("JFrame");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JScrollPane jMainScrollPane = getJMainScrollPane();
        this.add(jMainScrollPane);
        this.setSize(480, 339);
        Action action = new AbstractAction("Toggle CO") {

            @Override
            public void actionPerformed(ActionEvent e) {
                ComponentOrientation co = jMainScrollPane.getComponentOrientation().isLeftToRight() ?
                        ComponentOrientation.RIGHT_TO_LEFT : ComponentOrientation.LEFT_TO_RIGHT;
                jMainScrollPane.applyComponentOrientation(co);
                jMainScrollPane.revalidate();
                jMainScrollPane.repaint();
            }
        };
        add(new JButton(action), BorderLayout.SOUTH);
    }

    private JScrollPane getJMainScrollPane() {
        JScrollPane jMainScrollPane = new JScrollPane(getJMainPanel());
        jMainScrollPane.setViewportBorder(BorderFactory
                .createLineBorder(Color.GREEN));
        jMainScrollPane
                .applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        return jMainScrollPane;
    }

    private JPanel getJMainPanel() {
        JPanel jMainPanel = new JPanel();
        jMainPanel.add(new JButton("just a button"));
        jMainPanel.setBorder(BorderFactory.createLineBorder(Color.RED));
        return jMainPanel;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new COInScrollPane().setVisible(true);

            }
        });
    }
}

The initial layout is completely wrong (as reported by OP), toggle the CO with the button twice - the Layout looks okay.

A quick and dirty hack might be to do just that in procduction context: after initial layout, force the CO into LToR to allow for correct coordinates then back to RToL.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文