首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >垂直JScrollPane内的水平JScrollPane

垂直JScrollPane内的水平JScrollPane
EN

Stack Overflow用户
提问于 2022-09-12 09:59:50
回答 3查看 76关注 0票数 0

下午好!有必要从事件列表(横向)中创建一个列表(垂直)。至少有两个问题:

  1. 事件列表(水平滚动)的区域扩展到面板的边界以外。显然,问题在于布局,但我找不到正确的组合;
  2. 水平滚动不起作用(可能是由于上面描述的问题),滚动条对于每一组事件必须是不同的。
代码语言:javascript
复制
import ivank.components.EventAdd;
 
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
 
public class WindowAddCameras extends JFrame {
    public static final List<JPanel> labels = new ArrayList<JPanel>();
 
    public WindowAddCameras() {
        super("Добавить камеру");
 
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
        JPanel panelButton = new JPanel();
 
        JButton addButton = new JButton("+");
        addButton.setFocusable(false);
        panelButton.add(addButton);
 
        JButton remButton = new JButton("-");
        remButton.setFocusable(false);
        panelButton.add(remButton);
 
        JPanel externalPanel = new JPanel();
        externalPanel.setLayout(new BorderLayout(0, 0));
        JScrollPane scrollPaneGroupEvent = new JScrollPane(
                externalPanel,
                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
        );
 
        JPanel internalPanel = new JPanel();
        internalPanel.setLayout(new GridLayout(0, 1, 0, 0));
 
        JScrollPane scrollPaneEvent = new JScrollPane(internalPanel);
        scrollPaneEvent.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
        scrollPaneEvent.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
 
        externalPanel.add(scrollPaneEvent, BorderLayout.NORTH);
        addButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int number = labels.size() + 1;
                EventAdd eventAdd = new EventAdd();
                Dimension labelSize = new Dimension(80, 80);
 
                //add event to group event
                Random rand = new Random();
                for(int a = 0; a < 20; a++) {
                    //random color border event for TEST
                    Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
                    eventAdd.createEventLabel("Камера " + number, labelSize, randomColor);
                }
 
                labels.add(eventAdd);
                internalPanel.add(eventAdd, BorderLayout.NORTH);
                scrollPaneGroupEvent.revalidate();
            }
        });
 
        remButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if(labels.size() > 0) {
                    int index = labels.size() - 1;
                    JPanel panel = labels.remove(index);
                    internalPanel.remove(panel);
                    internalPanel.repaint();
                    scrollPaneGroupEvent.revalidate();
                }
            }
        });
 
        this.getContentPane().setLayout(new BorderLayout());
        this.getContentPane().add(panelButton, BorderLayout.NORTH);
        this.getContentPane().add(scrollPaneGroupEvent, BorderLayout.CENTER);
 
        this.setPreferredSize(new Dimension(600, 400));
        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }
}
代码语言:javascript
复制
package ivank.components;
 
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
 
public class EventAdd extends JPanel {
    public EventAdd() {
        super(new FlowLayout(FlowLayout.LEFT));
    }
 
    public JComponent createEventLabel(String name, Dimension labelSize, Color randomColor) {
        this.setBorder(BorderFactory.createTitledBorder(name));
        JLabel label = new JLabel();
        label.setPreferredSize(labelSize);
        label.setHorizontalAlignment(JLabel.CENTER);
 
        label.setBorder(BorderFactory.createLineBorder(randomColor, 5));
        this.add(label);
 
        return label;
    }
}

我拥有的:在这里输入图像描述我想要得到的:在这里输入图像描述

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-09-12 14:24:01

另一个滚动区域内的滚动区域是用户界面反模式(反设计?)。应该避免这种情况。

我将创建一个基于垂直BoxLayout的可滚动面板:

代码语言:javascript
复制
public class WindowAddCameras extends JFrame {
    private static final long serialVersionUID = 1;

    public static final List<JPanel> labels = new ArrayList<JPanel>();

    private static class CameraListPanel
    extends JPanel
    implements Scrollable {
        private static final long serialVersionUID = 1;

        CameraListPanel() {
            setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        }

        @Override
        public Dimension getPreferredScrollableViewportSize() {
            return getPreferredSize();
        }

        @Override
        public boolean getScrollableTracksViewportWidth() {
            return true;
        }

        @Override
        public boolean getScrollableTracksViewportHeight() {
            return false;
        }

        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect,
                                              int orientation,
                                              int direction) {
            return getScrollableIncrement(30,
                visibleRect, orientation, direction);
        }

        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect,
                                               int orientation,
                                               int direction) {
            return getScrollableIncrement(
                orientation == SwingConstants.HORIZONTAL ?
                    getWidth() : getHeight(),
                visibleRect, orientation, direction);
        }

        private int getScrollableIncrement(int amount,
                                           Rectangle visibleRect,
                                           int orientation,
                                           int direction) {
            if (orientation == SwingConstants.HORIZONTAL) {
                return Math.min(amount, direction < 0 ? visibleRect.x :
                    getWidth() - (visibleRect.x + visibleRect.width));
            } else {
                return Math.min(amount, direction < 0 ? visibleRect.y :
                    getHeight() - (visibleRect.y + visibleRect.height));
            }
        }
    }
 
    public WindowAddCameras() {
        super("Добавить камеру");
 
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
        JPanel panelButton = new JPanel();
 
        JButton addButton = new JButton("+");
        addButton.setFocusable(false);
        panelButton.add(addButton);
 
        JButton remButton = new JButton("-");
        remButton.setFocusable(false);
        panelButton.add(remButton);

        JPanel camerasPanel = new CameraListPanel();
        JScrollPane scrollPaneGroupEvent = new JScrollPane(camerasPanel);
 
        addButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int number = labels.size() + 1;
                EventAdd eventAdd = new EventAdd();
                Dimension labelSize = new Dimension(80, 80);
 
                //add event to group event
                Random rand = new Random();
                for(int a = 0; a < 20; a++) {
                    //random color border event for TEST
                    Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
                    eventAdd.createEventLabel("Камера " + number, labelSize, randomColor);
                }
 
                labels.add(eventAdd);
                camerasPanel.add(eventAdd);
                scrollPaneGroupEvent.revalidate();
            }
        });
 
        remButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if(labels.size() > 0) {
                    int index = labels.size() - 1;
                    JPanel panel = labels.remove(index);
                    camerasPanel.remove(panel);
                    camerasPanel.repaint();
                    scrollPaneGroupEvent.revalidate();
                }
            }
        });
 
        this.getContentPane().setLayout(new BorderLayout());
        this.getContentPane().add(panelButton, BorderLayout.NORTH);
        this.getContentPane().add(scrollPaneGroupEvent, BorderLayout.CENTER);
 
        this.setPreferredSize(new Dimension(600, 400));
        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> new WindowAddCameras());
    }
}

CameraListPanel类主要是一个基本的可滚动面板;重要的是getScrollableTracksViewportWidth()返回true,这将导致面板的宽度与任何JScrollPane视图端口的宽度相匹配。这消除了对水平滚动条的任何需求。

当然,您会希望以某种方式显示所有的子组件。为此,我将让EventAdd类计算一个能够容纳所有子级的高度:

代码语言:javascript
复制
public class EventAdd extends JPanel {
    private static final long serialVersionUID = 1;

    private final FlowLayout layout;

    public EventAdd() {
        layout = new FlowLayout(FlowLayout.LEFT);
        setLayout(layout);
    }

    @Override
    public Dimension getPreferredSize() {
        Rectangle childSize = new Rectangle();
        Component[] children = getComponents();
        for (Component child : children) {
            childSize.add(new Rectangle(child.getPreferredSize()));
        }

        Insets insets = getInsets();

        int hgap = layout.getHgap();
        int vgap = layout.getVgap();
        int childWidth = childSize.width + hgap;

        Dimension size;
        if (getParent() == null) {
            size = new Dimension(
                children.length * (childWidth * hgap) + hgap,
                childSize.height + vgap * 2);
        } else {
            int width = getParent().getWidth();
            width -= insets.left + insets.right;
            int childrenPerRow =
                childWidth == 0 ? 0 : (width - hgap) / childWidth;

            int rows;
            if (childrenPerRow == 0) {
                rows = 0;
            } else {
                rows = children.length / childrenPerRow;
                if (children.length % childrenPerRow > 0) {
                    rows++;
                }
            }
            size = new Dimension(width,
                vgap + rows * (childSize.height + vgap));
        }

        size.width += insets.left + insets.right;
        size.height += insets.top + insets.bottom;

        return size;
    }
票数 1
EN

Stack Overflow用户

发布于 2022-09-12 10:45:55

必须使用JScrollPane窗格方法setViewportView.为jscrollpane分配一个JViewport。您还应该给它一个滚动条策略setVerticalScrollBarPolicy setHorizontalScrollBarPolicy策略int是前两个方法的类的一部分,称为“常量字段值”,请参阅JScrollPane的API。要扩展JScrollPane,请将其放入带有BorderLayout的JPanel中,而在该特定面板中没有其他元素与JScrollPane一起使用。在JPanel上使用setMinimumSize和setPreferredSize以及主窗口中的setSize或BoxLayout (虽然我认为主窗口有特殊的布局,如果需要则不需要盒布局),或者堆栈中的更高级别的面板,或者仅用于JScrollPane的setPreferredSize。jscrollpane窗格有一个由它的"setView“方法设置的视图,它的视图是一个java.awt.Component注释:JComponent是 java.awt.Component,任何javax swing J面板或JComponent都是按层次划分的java.awt.Component。注意,jviewport是一个java.awt.Component,因此jscrollpane.setViewportView将jviewport引用jviewport.setView添加为带有BorderLayout的JPanel,添加另一个面板以开始添加其他组件,但请记住让所有jviewport都使用.preferredSize方法。如果您想要的是javax.swing.JList和javax.swing.DefaultListModel,那么必须将JList放在带有BorderLayout的JPanel中,或者更困难的系统( java.awt.GridBagLayout )中,然后放在jscrollpane窗格中。

票数 0
EN

Stack Overflow用户

发布于 2022-09-12 11:18:35

您可以只使用一个JScrollPane进行水平和垂直滚动。但是,您需要的是一个基础JComponent来呈现您的内容。然而,这可以是一个JPanel,它可以延伸到您需要的范围内。

看看如何使用ScrollPanes

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73687515

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档