You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

垂直JScrollPane内嵌水平JScrollPane布局与滚动问题求助

问题描述
  • 水平事件列表区域超出面板边界,推测是布局组合问题,但未找到合适方案;
  • 水平滚动功能失效(大概率因上述问题导致),且每组事件需配备独立滚动条。

相关代码

WindowAddCameras.java

import ivank.components.EventAdd;
 
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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);
    }
}

EventAdd.java

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

效果说明

  • 当前效果:水平排列的事件项超出容器右侧边界,无法滚动查看;垂直列表布局挤压事件区域,显示异常。
  • 预期效果:每组水平事件列表被包裹在独立的可水平滚动区域内,不会超出面板边界;垂直列表可正常上下滚动,每组事件拥有独立的水平滚动条。

解决方案

核心问题分析

  1. 原代码给所有事件组共用一个水平滚动条,未给每组事件单独分配滚动容器,导致滚动逻辑失效。
  2. EventAdd中重复设置标题边框,且未正确约束组件对齐方式,导致布局计算异常,内容超出边界。

修改后代码

WindowAddCameras.java

import ivank.components.EventAdd;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class WindowAddCameras extends JFrame {
    public static final List<JScrollPane> eventScrollPanes = new ArrayList<>();

    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(new BorderLayout());
        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, 5)); // 增加垂直间距优化视觉
        externalPanel.add(internalPanel, BorderLayout.NORTH);

        addButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int number = eventScrollPanes.size() + 1;
                EventAdd eventAdd = new EventAdd();
                Dimension labelSize = new Dimension(80, 80);

                // 仅设置一次标题边框,避免重复叠加
                eventAdd.setBorder(BorderFactory.createTitledBorder("Камера " + number));

                Random rand = new Random();
                for(int a = 0; a < 20; a++) {
                    Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
                    eventAdd.createEventLabel(labelSize, randomColor);
                }

                // 为每组事件创建独立的水平滚动容器
                JScrollPane singleEventScroll = new JScrollPane(eventAdd);
                singleEventScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
                singleEventScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

                eventScrollPanes.add(singleEventScroll);
                internalPanel.add(singleEventScroll);
                scrollPaneGroupEvent.revalidate();
                scrollPaneGroupEvent.repaint();
            }
        });

        remButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if(eventScrollPanes.size() > 0) {
                    int index = eventScrollPanes.size() - 1;
                    JScrollPane scrollPane = eventScrollPanes.remove(index);
                    internalPanel.remove(scrollPane);
                    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);
    }
}

EventAdd.java

package ivank.components;

import javax.swing.*;
import java.awt.*;

public class EventAdd extends JPanel {
    public EventAdd() {
        super(new FlowLayout(FlowLayout.LEFT, 5, 5)); // 设置水平/垂直间距
        this.setAlignmentX(LEFT_ALIGNMENT); // 确保垂直布局时宽度被正确约束
    }

    public JComponent createEventLabel(Dimension labelSize, Color randomColor) {
        JLabel label = new JLabel();
        label.setPreferredSize(labelSize);
        label.setHorizontalAlignment(JLabel.CENTER);
        label.setBorder(BorderFactory.createLineBorder(randomColor, 5));
        this.add(label);
        return label;
    }
}

修改要点说明

  1. 独立滚动容器:移除全局滚动条,为每个EventAdd实例单独创建JScrollPane,实现每组事件独立水平滚动。
  2. 修复布局异常:给EventAdd设置LEFT_ALIGNMENT,确保垂直布局中组件宽度被容器正确约束,避免内容溢出。
  3. 优化重复操作:将标题边框的设置移到循环外,避免每次添加标签都重复设置边框。
  4. 视觉优化:调整GridLayout的垂直间距,提升界面美观度。
  5. 修正存储对象:将列表存储对象改为JScrollPane,确保删除操作能正确移除对应的滚动容器组件。

内容的提问来源于stack exchange,提问作者IvanNaStackOF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 17:45:40