简体   繁体   English

在循环中使用Java Swing时出现问题

[英]Issue when using Java Swing in a For-Loop

I have created a simple for-loop that changes the amount of JTextFields and JLabels based on the value of a JSpinner, as seen in the following code: 我创建了一个简单的for循环,它根据JSpinner的值更改JTextField和JLabel的数量,如以下代码所示:

        public static ArrayList<ArrayList<JTextField>> ChangeQuestionAnswerFields(int numberOfQuestions){
        ArrayList<ArrayList<JTextField>> txtFieldArray = new ArrayList<ArrayList<JTextField>>();

        JPanel scrollPanel = new JPanel(new GridBagLayout());
        //JScrollPane scrollPane = new JScrollPane(scrollPanel);
        frame.add(scrollPanel, BorderLayout.CENTER);
        GridBagConstraints c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 0;


        for(int i = 0; i != numberOfQuestions; i++){
            JTextField tempQuestion = new JTextField(10);
            JTextField tempAnswer = new JTextField(10);
            JLabel tempQuestionHeader = new JLabel("Question " + (i + 1)+ " ");
            JLabel tempQuestionLbl = new JLabel("Question: ");
            JLabel tempAnswerLbl = new JLabel("Answer: ");

            ArrayList<JTextField> tempArrayList = new ArrayList<>();
            tempArrayList.add(tempQuestion);
            tempArrayList.add(tempAnswer);
            txtFieldArray.add(tempArrayList);

            c.gridy++;
            c.gridx = 0;
            scrollPanel.add(tempQuestionHeader, c);

            c.gridy++;
            c.gridx = 0;
            scrollPanel.add(tempQuestionLbl, c);

            c.gridx = 1;
            c.gridwidth = 3;
            scrollPanel.add(tempQuestion, c);

            c.gridy++;
            c.gridx = 0;
            scrollPanel.add(tempAnswerLbl, c);

            c.gridx = 1;
            c.gridwidth = 3;
            scrollPanel.add(tempAnswer, c);
        }
        return txtFieldArray;
    }
}

The value of the Spinner is passed into the method, and the method is called using a change listener (where noQuestions is the value of the JSpinner): Spinner的值传递到方法中,并使用更改侦听器(其中noQuestions是JSpinner的值)调用该方法:

noQuestions.addChangeListener(e -> {
        ChangeQuestionAnswerFields((int) noQuestions.getValue());
        frame.revalidate();
        frame.repaint();
    });

This method is first called in the code when the screen first appears, and works properly. 屏幕首次出现时,该方法首先在代码中调用,并且可以正常工作。 However, whenever the value of the spinner changes the original labels and fields stay on the screen and more text fields simply appear, or disappear on top. 但是,每当微调器的值更改时,原始标签和字段就会保留在屏幕上,而更多文本字段只会出现或消失在顶部。

http://i.imgur.com/GBY8L3u.png - JSpinner has a value of 2 http://i.imgur.com/pSQsA3G.png - JSpinner has a value of 3 http://i.imgur.com/GBY8L3u.png-JSpinner的值为2 http://i.imgur.com/pSQsA3G.png-JSpinner的值为3

Is there any way to fix this? 有没有什么办法解决这一问题? Any help is much appreciated. 任何帮助深表感谢。

Thanks, Tom 谢谢汤姆

Minimal Runnable Example: 最小可运行示例:

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;

public class MainGUI {
    static JFrame frame = new JFrame("Math Reviser");

    public static void main(String[] args) {


        frame.setSize(400, 600);
        frame.setVisible(true);

        createScreen();
        frame.revalidate();
        frame.repaint();

    public static void createScreen(){
        frame.getContentPane().removeAll();

        JSpinner noQuestions = new JSpinner(new SpinnerNumberModel(1, 1, 10, 1));
        frame.add(noQuestions, BorderLayout.NORTH);
        );
        changeQuestionAnswerFields(1);

        frame.revalidate();
        frame.repaint();

        noQuestions.addChangeListener(e -> {
            changeQuestionAnswerFields((int) noQuestions.getValue());
            frame.revalidate();
            frame.repaint();
        });

    }

    public static ArrayList<ArrayList<JTextField>> changeQuestionAnswerFields(int numberOfQuestions){
        ArrayList<ArrayList<JTextField>> txtFieldArray = new ArrayList<ArrayList<JTextField>>();

        JPanel scrollPanel = new JPanel(new GridBagLayout());
        frame.add(scrollPanel, BorderLayout.CENTER);
        GridBagConstraints c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 0;


        for(int i = 0; i != numberOfQuestions; i++){
            JTextField tempQuestion = new JTextField(10);
            JTextField tempAnswer = new JTextField(10);
            JLabel tempQuestionHeader = new JLabel("Question " + (i + 1)+ " ");
            JLabel tempQuestionLbl = new JLabel("Question: ");
            JLabel tempAnswerLbl = new JLabel("Answer: ");

            ArrayList<JTextField> tempArrayList = new ArrayList<>();
            tempArrayList.add(tempQuestion);
            tempArrayList.add(tempAnswer);
            txtFieldArray.add(tempArrayList);

            c.gridy++;
            c.gridx = 0;
            scrollPanel.add(tempQuestionHeader, c);

            c.gridy++;
            c.gridx = 0;
            scrollPanel.add(tempQuestionLbl, c);

            c.gridx = 1;
            c.gridwidth = 3;
            scrollPanel.add(tempQuestion, c);

            c.gridy++;
            c.gridx = 0;
            scrollPanel.add(tempAnswerLbl, c);

            c.gridx = 1;
            c.gridwidth = 3;
            scrollPanel.add(tempAnswer, c);
        }
        return txtFieldArray;
    }
}

Using static variables and methods is an indication of a poorly designed application. 使用静态变量和方法表明应用程序设计不良。 There is no need for the static variables or methods. 不需要静态变量或方法。 I suggest you read the section from the Swing tutorial on How to Use Labels . 建议您阅读Swing教程中有关如何使用标签的部分 The LabelDemo.java code will show you how to create a panel containing all the components. LabelDemo.java代码将向您展示如何创建一个包含所有组件的面板。 This panel will then be added to the frame. 然后,此面板将添加到框架。 This panel will also contain all the instance variables you need for your program. 该面板还将包含程序所需的所有实例变量。

Not only that the example will show you how to create the GUI components on the EDT which is something you should always do to prevent random errors since Swing was designed to be single threaded. 该示例不仅将向您展示如何在EDT上创建GUI组件,这是您应始终执行的操作,以防止随机错误,因为Swing被设计为单线程。

However, the main problem with your existing code is that you continue to create and add new panels to the content pane of the frame. 但是,现有代码的主要问题是您继续创建新面板并将其添加到框架的内容窗格。 Try changing the spinner to 2 and then resize the frame. 尝试将微调器更改为2,然后调整框架的大小。 Then try changing the spinner to 3 and resize the frame. 然后尝试将微调器更改为3并调整框架的大小。 After the resizing the first panel is displayed. 调整大小后,将显示第一个面板。 This is because Swing will paint the last component added first so the first panel added will be painted on top of the last panel you created. 这是因为Swing将绘制首先添加的最后一个组件,因此添加的第一个面板将被绘制在您创建的最后一个面板的顶部。

You can change this in your existing code by removing the old panel before adding the new panel: 您可以通过在添加新面板之前删除旧面板来更改现有代码:

static JFrame frame = new JFrame("Math Reviser");
static JPanel scrollPanel = new JPanel();
...
frame.remove(scrollPanel);
//JPanel scrollPanel = new JPanel(new GridBagLayout());
scrollPanel = new JPanel(new GridBagLayout());

However, I do not recommend this approach. 但是,我不推荐这种方法。 As I initially suggestion you need to redesign the entire class. 正如我最初建议的那样,您需要重新设计整个课程。 When you do the redesign I would use a BorderLayout on your panel and then you can add your spinner to the PAGE_START and then add a JScrollPane to the CENTER of the panel. 当您进行重新设计时,我将在面板上使用BorderLayout,然后可以将微调器添加到PAGE_START,然后将JScrollPane添加到面板的CENTER。

Then when you want to create a new panel you add the panel to the scrollpane using code like: 然后,当您要创建新面板时,可以使用以下代码将面板添加到滚动窗格中:

scrollPane.setViewportView( scrollPanel );

The scrollpane will refresh itself and you don't need to worry about revalidate() or repaint() or anything else. 滚动面板会自动刷新,您无需担心revalidate()或repaint()或其他任何问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM