拆分窗格、滚动窗格和布局如何工作

Posted

技术标签:

【中文标题】拆分窗格、滚动窗格和布局如何工作【英文标题】:How do split panes, scroll panes & layouts work 【发布时间】:2021-07-22 08:13:54 【问题描述】:

我正在尝试在 Java swing 中制作这种 UI,您可以在其中将内容从左侧菜单拖放到中间的一个单元格中,就像这张左右两侧可折叠的图像:

What I'm trying to make

我目前的尝试让我处于这个奇怪的阶段: What actually happens

更奇怪的是,我已经放入左侧面板的内容只有在我将鼠标悬停在它上面时才会出现,并且在我使用拆分调整它的大小后它会再次消失。此外,只有一个我添加到面板的面板出现,而第二个面板无法出现: After moving divider Mouse moved over button now appears

在我的图表区域中,按钮拒绝移动到图表的顶部,除非我移动分隔线以使面板足够小以将其强制到顶部。相关代码在这里:

    import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.filechooser.FileNameExtensionFilter;

import java.awt.*;
import java.awt.event.*;
import java.io.*;

import org.jfree.data.time.DynamicTimeSeriesCollection;
import org.jfree.data.time.Second;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.xy.XYDataset;

/**
 * @author CephDigital
 *
 */
public class GUI extends JFrame
    
    DynamicTimeSeriesCollection dataset;
    private Timer timer;
    ActionListener timerAction = new ActionListener() 
        float newData[] = new float[1];
        
        @Override
        public void actionPerformed(ActionEvent e) 
            //Store new data to newData
            dataset.advanceTime();
            dataset.appendData(newData);
        
    ;
    
    int size = 20;
    DefaultComponent[][] graph = new DefaultComponent[size][size];
    
    String saveDirectory = "";
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    int screenWidth = (int)screenSize.getWidth(),
        screenHeight = (int)screenSize.getHeight();
    
    JButton btnGraphStart = new JButton("Place first point"),
            btnGraphEnd = new JButton("Place end point"),
            hidePower = new JButton("Power"),
            hideResist = new JButton("Resistors"),
            startSimulation = new JButton("Start"),
            endSimulation = new JButton("End");
    
    JMenuBar optionsBar = new JMenuBar();
    JMenu fileMenu = new JMenu("File");
    JMenuItem menuItem;
    
    JPanel  topMenuPanel = new JPanel(),
            //compPanel = new JPanel(),
            gridPanel = new JPanel(),
            graphpanel = new JPanel(),
            itemPanel,
            globalPanel = new JPanel(),
            resistorComps = new JPanel(),
            resistorTotal = new JPanel(),
            powerComps = new JPanel(),
            powerTotal = new JPanel();
    
    JScrollPane compPanel = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    
    JToolBar simulationControl = new JToolBar("Simulation Control");
    
    JSlider light;
    JLabel lightTxt;
    
    public GUI() 
        //Setting up split panels
        getContentPane().setLayout(new BorderLayout());
        
        compPanel.setBackground(Color.BLUE);
        gridPanel.setBackground(Color.CYAN);
        gridPanel.setLayout(new GridLayout(size,size));
        graphPanel.setBackground(Color.GREEN);
        
        
        JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, compPanel, gridPanel);
        sp.setOneTouchExpandable(true);
        sp.setMinimumSize(new Dimension(200, 400));
        //sp.setDividerLocation(500);
        JSplitPane sp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sp, graphPanel);
        sp2.setOneTouchExpandable(true);
        //sp2.setDividerLocation(700);
        
        //Setup the menu for the components panel
        hideResist.addActionListener(new ActionListener() 
            public void actionPerformed(ActionEvent e) 
                if (resistorComps.isVisible()) 
                    resistorComps.setVisible(false);
                 else 
                    resistorComps.setVisible(true);
                
            
        );
        hidePower.addActionListener(new ActionListener() 
            public void actionPerformed(ActionEvent e) 
                if (powerComps.isVisible()) 
                    powerComps.setVisible(false);
                 else 
                    powerComps.setVisible(true);
                
            
        );
        
        //Panel for different power IO
        powerComps.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
        powerTotal.setLayout(new GridLayout(0, 1));
        powerTotal.add(hidePower);
        powerTotal.add(powerComps);
        powerTotal.setSize(200, 200);
        compPanel.add(powerTotal);
        
        //Panel for different types of resistors
        resistorComps.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
        resistorTotal.setLayout(new GridLayout(0, 1));
        resistorTotal.add(hideResist);
        resistorTotal.add(resistorComps);
        powerTotal.setSize(200, 200);
        compPanel.add(resistorTotal);
        
        //compPanel.setLayout(new GridLayout(1,2));
        //compScroll.add(compPanel);
        
        //Setup the grid panel
        for (int x = 0; x < size; x++) 
            for (int y = 0; y < size; y++) 
                //Create a panel
                itemPanel = new JPanel();
                itemPanel.setPreferredSize(new Dimension(300, 300));
                itemPanel.setSize(100, 100);
                itemPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
                gridPanel.add(itemPanel);
            
        
        
        //Setup the graph panel
        float[] voltageData = ;
        dataset = new DynamicTimeSeriesCollection(1, 2 * 60, new Second());
        dataset.setTimeBase(new Second(0, 0, 0, 1, 1, 2011));
        dataset.addSeries(voltageData, 0, "Voltage");
        JFreeChart chart = createChart(dataset);
        
        JPanel buttonPanel = new JPanel(new FlowLayout());
        buttonPanel.add(btnGraphStart);
        buttonPanel.add(btnGraphEnd);
        
        graphPanel.setLayout(new BorderLayout());
        graphPanel.add(buttonPanel, BorderLayout.PAGE_START);
        graphPanel.add(new ChartPanel(chart) 
            @Override
            public Dimension getPreferredSize() 
                return new Dimension(320, 240);
            
        , BorderLayout.CENTER);
        
        //Setup the global panel for global variables that the user can change
        light = new JSlider(1, 100000, 1);
        light.addChangeListener((ChangeEvent e) -> 
            float text = (float)(light.getValue()) / 10;
            lightTxt.setText(Float.toString(text));
        );
        lightTxt = new JLabel("0.1");
        createLayout(globalPanel, new JLabel("Global Lux"), light, lightTxt);
        
        //Set the location of components in the frame
        this.setTitle("Circuit Simulator");
        this.add(topMenuPanel, BorderLayout.NORTH);
        this.add(sp2, BorderLayout.CENTER);
        this.add(globalPanel, BorderLayout.SOUTH);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(1243, 679);
        this.setVisible(true);
    

    public static void main(String[] args) 
        new GUI();
    
    
    private JFreeChart createChart(XYDataset dataset) 
        JFreeChart result = ChartFactory.createTimeSeriesChart(
                "Title", "mm:ss", "Voltage", dataset, true, true, false);
        XYPlot plot = result.getXYPlot();
        ValueAxis domain = plot.getDomainAxis();
        domain.setAutoRange(true);
        ValueAxis range = plot.getRangeAxis();
        range.setRange(0, 10);
        return result;
    
    
    private void createLayout(JComponent...arg) 
        JPanel panel = (JPanel) arg[0];
        GroupLayout gl = new GroupLayout(panel);
        panel.setLayout(gl);
        
        gl.setAutoCreateContainerGaps(true);
        gl.setAutoCreateGaps(true);
        
        gl.setHorizontalGroup(gl.createParallelGroup()
                .addComponent(arg[1])
                .addComponent(arg[2])
                .addComponent(arg[3])
        );
        
        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(arg[1])
                .addComponent(arg[2])
                .addComponent(arg[3])
        );
        
        pack();
    

我讨厌 GUI 制作,所以请有人帮我弄清楚这些废话。

【问题讨论】:

编辑您的问题并包含创建所有这些面板并设置其布局的代码。我们需要minimal reproducible example,以便我们自己查看您的问题。也就是说,您可能想阅读docs.oracle.com/javase/tutorial/uiswing/layout 以清楚地了解布局。 好的,代码已正确编辑。现在应该可以重现问题了。 这不是minimal reproducible example。您的问题是关于组件的布局。使用来自 JFreeChart 的组件与这个问题无关。您正在使用我无法访问的第 3 方课程。您可以只使用 JLabel 或其他东西来表示图表。无论如何,您的最后一个问题是为什么左侧的组件不显示。您似乎错过了关于一步一步解决问题的评论。您的 MRE 在哪里,它只是在拆分窗格中显示左侧面板?此外,所有 ActionListener 都与所述问题无关。监听器不会影响布局。 @CephDigital 菜单栏与所述问题无关。使用适当的 MRE,整个代码可能只有 30 行代码。几行来创建框架。一对夫妇创建拆分面板。还有一些可以为左侧组件创建嵌套面板。下面是一个使用拆分窗格处理问题的 MRE 示例。您应该可以对其进行修改以创建“左侧面板”,以尝试使其以您想要的方式显示:***.com/a/45929359/131872。一旦你得到一个基本的例子,你就可以修复你的真实应用程序。 【参考方案1】:

默认情况下,JPanel 使用 FlowLayout。您的代码不完整,但在我看来,您的 compPanel 和 graphPanel 都在使用默认布局管理器,因此布局不是您所期望的。

您需要在必要时设置相应的布局管理器和嵌套面板,以达到您想要的效果。

例如,对于 graphPanel,我建议您可以执行以下操作:

JPanel buttonPanel = new JPanel( new FlowLayout(...) ); // center aligned
buttonPanel.add(btnGraphStart);
buttonPanel.add(btnGraphEnd);

ChartPanel chartPanel = new ChartPane(chart);

graphPanel.setLayout( new BorderLayout() );
graphPanel.add(buttonPanel, BorderLayout.PAGE_START);
graphPanel.add(chartPanel, BorderLayout.CENTER);

所以现在按钮将连续显示在顶部,图表将显示在按钮下方。

也为 compPanel 使用适当的布局。也许使用垂直的BoxLayout,然后你可以添加两个子面板。

【讨论】:

很好,解决了按钮问题,谢谢!左侧面板上有什么建议吗?我觉得奇怪的是,当我将鼠标悬停在面板上时,当代码中没有任何内容可以做到这一点时,它才会显示面板。 我提出了一个建议。你还没有发布你的minimal reproducible example。因此,首先创建一个仅将该面板添加到拆分窗格的 JFrame。我永远无法弄清楚为什么人们会在测试之前编写整个代码。所以我的建议是从简单开始并进行测试。然后添加更多代码并测试。那就是创建一个面板并测试它是否按预期显示。然后创建另一个并重新测试等。调试较小的代码块要容易得多。 我更改了代码,现在可以重现了。 解决了按钮问题 - 很高兴它有所帮助。不要忘记“接受”答案,这样人们就知道问题已经解决了。见:What should I do when someone answers my questiojn

以上是关于拆分窗格、滚动窗格和布局如何工作的主要内容,如果未能解决你的问题,请参考以下文章

表格怎么冻结前两行

Excel2017如何同时冻结首行首列不滚动

如何将滚动窗格内容保存为 jpg 文件

Java gridbaglayout 使用带有可拖动 jpanel 的滚动窗格

如何使用类似于 chart.io 和 simple.com 的平滑滚动窗格创建动画菜单

使用poi导出的excel怎么设置表头不动