如何让JScrollPane添加组件后动态滚动到最后?

发布于 2025-01-09 04:36:39 字数 1002 浏览 4 评论 0原文

这是我尝试执行的 ScrollPane 代码

    public class CustomScrollPane extends JScrollPane {

    private static CustomScrollPane instance = null;

    public CustomScrollPane () {
        super(panel.getInstance()); // a panel that the scrollpane wraps around
        this.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        // hide the vertical scroll bar
        this.getVerticalScrollBar().setPreferredSize(new Dimension(0, 0));
    }

    public static CustomScrollPane getInstance() {
        if (instance == null)
            instance = new CustomScrollPane ();
        return instance;
    }

getVerticalScrollbar.setValue(getVerticalScrollbar().getMaximum()) 但当我向其中添加 JLabels 时,它不会滚动到末尾。我正在尝试使其在新的 JLabel 添加到面板后始终滚动到屏幕底部。我没有使用 JTextArea,因为我希望每行都有不同的前景色,所以我使用了 JLabels。

我也尝试添加此方法

public void scrollToBottom() { getVerticalScrollbar().getMaximum(); }

,但它只是冻结 ScrollPane 并且我无法滚动。

任何帮助将不胜感激!

Here is my code for the ScrollPane

    public class CustomScrollPane extends JScrollPane {

    private static CustomScrollPane instance = null;

    public CustomScrollPane () {
        super(panel.getInstance()); // a panel that the scrollpane wraps around
        this.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        // hide the vertical scroll bar
        this.getVerticalScrollBar().setPreferredSize(new Dimension(0, 0));
    }

    public static CustomScrollPane getInstance() {
        if (instance == null)
            instance = new CustomScrollPane ();
        return instance;
    }

I tried to do getVerticalScrollbar.setValue(getVerticalScrollbar().getMaximum()) but it does not scroll to the end when I add JLabels to it. I'm trying to make it so that it will always scroll to the bottom of the screen once a new JLabel gets added to the panel. I din't use JTextArea as I want each line to have a different foreground color, so I used JLabels.

I also tried adding this method

public void scrollToBottom() { getVerticalScrollbar().getMaximum(); }

but it just freezes the ScrollPane and I am unable to scroll.

Any help would be appreciated!

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

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

发布评论

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

评论(1

面如桃花 2025-01-16 04:36:39

我不确定你希望通过扩展 JScrollPane 实现什么,不确定这是否真的是它的核心职责,毕竟,你可以让它的“视口视图”自己完成,例如......

输入图片描述这里

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new BorderLayout());
            JPanel contentPane = new JPanel(new GridLayout(-1, 8));
            for (int index = 0; index < 1000; index++) {
                contentPane.add(new SqaurePane());
            }
            add(new JScrollPane(contentPane));

            JButton top = new JButton("Top");
            JButton bottom = new JButton("Bottom");

            JPanel actionPane = new JPanel();
            actionPane.add(top);
            actionPane.add(bottom);

            add(actionPane, BorderLayout.SOUTH);

            top.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    contentPane.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
                }
            });
            bottom.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    contentPane.scrollRectToVisible(new Rectangle(0, contentPane.getHeight(), 0, 0));
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }
    }

    static List<Color> COLORS = new ArrayList<>(Arrays.asList(new Color[] {
        Color.BLACK,
        Color.BLUE,
        Color.CYAN,
        Color.DARK_GRAY,
        Color.GRAY,
        Color.GREEN,
        Color.LIGHT_GRAY,
        Color.MAGENTA,
        Color.ORANGE,
        Color.PINK,
        Color.RED,
        Color.WHITE,
        Color.YELLOW,
    }));

    public class SqaurePane extends JPanel {

        public SqaurePane() {
            Collections.shuffle(COLORS);
            setBackground(COLORS.get(0));
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(50, 50);
        }        
    }
}

魔法就在这里...

top.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        contentPane.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
    }
});
bottom.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        contentPane.scrollRectToVisible(new Rectangle(0, contentPane.getHeight(), 0, 0));
    }
});

在这里,我要求 contentPane (它是容纳所有方块的容器)基于以下内容“滚动到可见的矩形”我的需求

现在,在您的情况下,当您将新组件添加到“容器”时,您需要指示组件滚动到显示新组件的位置。

这会有点复杂,因为您需要首先触发布局过程,这样您就可以获得容器的新大小

动态添加到容器中...

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new BorderLayout());
            JPanel contentPane = new JPanel(new GridLayout(-1, 8));
            add(new JScrollPane(contentPane));

            for (int index = 0; index < 8 * 8; index++) {
                contentPane.add(new SqaurePane());
            }

            Timer timer = new Timer(500, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    SqaurePane sqaurePane = new SqaurePane();
                    contentPane.add(sqaurePane);
                    contentPane.revalidate();
                    // There is an issue with how the layout pass runs, this 
                    // "seems" to be getting pushed onto the EDT later, which
                    // is messing up the scroll logic.
                    // So, instead, we push this on to the EDT to be executed
                    // "later" after the layout pass has run.  Yes, I tried
                    // calling doLayout directly, but, for the first element
                    // of each row, it wouldn't work correctly
                    EventQueue.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            contentPane.scrollRectToVisible(new Rectangle(0, sqaurePane.getY(), 1, sqaurePane.getHeight()));
                        }
                    });
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }
    }

    static List<Color> COLORS = new ArrayList<>(Arrays.asList(new Color[]{
        Color.BLACK,
        Color.BLUE,
        Color.CYAN,
        Color.DARK_GRAY,
        Color.GRAY,
        Color.GREEN,
        Color.LIGHT_GRAY,
        Color.MAGENTA,
        Color.ORANGE,
        Color.PINK,
        Color.RED,
        Color.WHITE,
        Color.YELLOW,}));

    public class SqaurePane extends JPanel {

        public SqaurePane() {
            Collections.shuffle(COLORS);
            setBackground(COLORS.get(0));
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(50, 50);
        }
    }
}

现在,如果您真的想解耦这个概念,您可以我们可以使用 ContainerListener 和对新组件做出反应通过它添加

I'm not sure what you hope to achieve by extending JScrollPane, not sure that it's really its core responsibility, after all, you could have its "viewport view" do it itself, for example...

enter image description here

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new BorderLayout());
            JPanel contentPane = new JPanel(new GridLayout(-1, 8));
            for (int index = 0; index < 1000; index++) {
                contentPane.add(new SqaurePane());
            }
            add(new JScrollPane(contentPane));

            JButton top = new JButton("Top");
            JButton bottom = new JButton("Bottom");

            JPanel actionPane = new JPanel();
            actionPane.add(top);
            actionPane.add(bottom);

            add(actionPane, BorderLayout.SOUTH);

            top.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    contentPane.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
                }
            });
            bottom.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    contentPane.scrollRectToVisible(new Rectangle(0, contentPane.getHeight(), 0, 0));
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }
    }

    static List<Color> COLORS = new ArrayList<>(Arrays.asList(new Color[] {
        Color.BLACK,
        Color.BLUE,
        Color.CYAN,
        Color.DARK_GRAY,
        Color.GRAY,
        Color.GREEN,
        Color.LIGHT_GRAY,
        Color.MAGENTA,
        Color.ORANGE,
        Color.PINK,
        Color.RED,
        Color.WHITE,
        Color.YELLOW,
    }));

    public class SqaurePane extends JPanel {

        public SqaurePane() {
            Collections.shuffle(COLORS);
            setBackground(COLORS.get(0));
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(50, 50);
        }        
    }
}

The magic is right here...

top.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        contentPane.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
    }
});
bottom.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        contentPane.scrollRectToVisible(new Rectangle(0, contentPane.getHeight(), 0, 0));
    }
});

Here, I'm asking the contentPane (which is the container holding all the squares) to "scroll to a visible rectangle" based on my needs

Now, in you case, when you add a new component to the "container", you would need to instruct the component to scroll to a position which would show the new component.

This is going to be a little more complicated as you will need to trigger a layout pass first, so you can get the new size of the container

Dynamic addition to the container...

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new BorderLayout());
            JPanel contentPane = new JPanel(new GridLayout(-1, 8));
            add(new JScrollPane(contentPane));

            for (int index = 0; index < 8 * 8; index++) {
                contentPane.add(new SqaurePane());
            }

            Timer timer = new Timer(500, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    SqaurePane sqaurePane = new SqaurePane();
                    contentPane.add(sqaurePane);
                    contentPane.revalidate();
                    // There is an issue with how the layout pass runs, this 
                    // "seems" to be getting pushed onto the EDT later, which
                    // is messing up the scroll logic.
                    // So, instead, we push this on to the EDT to be executed
                    // "later" after the layout pass has run.  Yes, I tried
                    // calling doLayout directly, but, for the first element
                    // of each row, it wouldn't work correctly
                    EventQueue.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            contentPane.scrollRectToVisible(new Rectangle(0, sqaurePane.getY(), 1, sqaurePane.getHeight()));
                        }
                    });
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }
    }

    static List<Color> COLORS = new ArrayList<>(Arrays.asList(new Color[]{
        Color.BLACK,
        Color.BLUE,
        Color.CYAN,
        Color.DARK_GRAY,
        Color.GRAY,
        Color.GREEN,
        Color.LIGHT_GRAY,
        Color.MAGENTA,
        Color.ORANGE,
        Color.PINK,
        Color.RED,
        Color.WHITE,
        Color.YELLOW,}));

    public class SqaurePane extends JPanel {

        public SqaurePane() {
            Collections.shuffle(COLORS);
            setBackground(COLORS.get(0));
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(50, 50);
        }
    }
}

Now, if you really wanted to decouple the concept, you could us a ContainerListener and react to the new component been added via it

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