简体   繁体   中英

swing gui to return user input

I wrote a small swing program to get user input (a sequence of 0/1 chars, followed by "Done") are return the string to the main class - code attached below. The problem is that it hangs when run in normal mode, but works fine when a breakpoint is put on the "return new String(str)" line (in function getData()), and single-step after that. I figured that it is a timing problem, and put in a "Thread.sleep(400)" before the while-loop (see commented lines) - and now it works fine.

But this code looks stupid. Is there a better way to write this code - to take user input and return the user-given string to the calling class?

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

class DataEntryPanel extends JPanel implements ActionListener {
    private JButton Button0, Button1, ButtonDone;
    private JLabel DataEntered;
    public char[] str = "________".toCharArray();
    int posn = 0;
    public boolean dataDone = false;
    public DataEntryPanel() {
        this.setLayout(new FlowLayout(FlowLayout.CENTER));
        Button0 = new JButton("0"); Button0.addActionListener(this); this.add(Button0);
        Button1 = new JButton("1"); Button1.addActionListener(this); this.add(Button1);
        ButtonDone = new JButton("Done"); ButtonDone.addActionListener(this); this.add(ButtonDone);
        DataEntered = new JLabel("xxxxxxxx"); this.add(DataEntered);
    }
    public void actionPerformed(ActionEvent e) {
        Object source  = e.getSource();
        if(source==Button0) DataEntered.setText(setData('0'));
        else if(source==Button1) DataEntered.setText(setData('1'));
        else if(source==ButtonDone) dataDone=true;
    }
    public String setData(char c) {
        if(posn<8) str[posn++] = c;
        return new String(str);
    }
}
class DataEntryFrame extends JFrame {
    public JPanel panel;
    private void centerWindow (Window w) {
        Toolkit tk = Toolkit.getDefaultToolkit();
        Dimension d = tk.getScreenSize();
        setLocation((d.width-w.getWidth())/2, (d.height-w.getHeight())/2);
    }
    public DataEntryFrame() {
        setTitle("Data Entry");
        setSize(267, 200);
        centerWindow(this);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        panel = new DataEntryPanel();
        this.add(panel);
    }
    public String getData() {
        DataEntryPanel p = (DataEntryPanel) panel;
        System.out.printf("waiting for data......\n");
        // try {
           while(!p.dataDone) 
                // Thread.sleep(400)
                ; // looping on data completion
        // } catch (InterruptedException e) { e.printStackTrace(); }
        return new String(p.str);
    }
}

public class FRead {
    public FRead() {
        JFrame frame  = new DataEntryFrame();
        frame.setVisible(true);
        DataEntryFrame f = (DataEntryFrame) frame;
        String s = f.getData();
        System.out.printf("string obtained=%s\n", s);
        System.exit(0);
    }

    public static void main(String[] args) throws Exception {
        new FRead();
    }
}

You are probably running into EDT-issues and eating up your CPU with your while-loop.

  1. Everything related to the UI should be performed on the EDT (Event Dispatching Thread)
  2. To center a frame, set its size (using pack() or setSize() ) and then simply call setLocationRelativeTo(null);
  3. Never do a while(!true) ; loop, this will eat up your CPU and block the current Thread.
  4. Your dataDone variable should be declared volatile because you are reading it within the "Main" Thread but it is modified by the EDT.

Consider reading about Concurrency in Swing

Here is a slightly modified version of your code that seems to work a lot better:

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class GUI {

    class DataEntryPanel extends JPanel implements ActionListener {
        private JButton button0, button1, buttonDone;
        private JLabel dataEntered;
        public char[] str = "________".toCharArray();
        int posn = 0;
        public volatile boolean dataDone = false;
        private String data;

        public DataEntryPanel() {
            this.setLayout(new FlowLayout(FlowLayout.CENTER));
            button0 = new JButton("0");
            button0.addActionListener(this);
            this.add(button0);
            button1 = new JButton("1");
            button1.addActionListener(this);
            this.add(button1);
            buttonDone = new JButton("Done");
            buttonDone.addActionListener(this);
            this.add(buttonDone);
            dataEntered = new JLabel("xxxxxxxx");
            this.add(dataEntered);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            Object source = e.getSource();
            if (source == button0) {
                dataEntered.setText(setData('0'));
            } else if (source == button1) {
                dataEntered.setText(setData('1'));
            } else if (source == buttonDone) {
                JOptionPane.showMessageDialog(this, "Data entered is " + String.format("string obtained=%s\n", getData()));
                System.exit(0);
            }
        }

        public String getData() {
            return data;
        }

        public String setData(char c) {
            if (posn < 8) {
                str[posn++] = c;
            }
            return data = new String(str);
        }

    }

    protected void initUI() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setTitle("Data Entry");
        frame.setSize(267, 200);
        frame.setLocationRelativeTo(null);
        DataEntryPanel panel = new DataEntryPanel();
        frame.add(panel);
        frame.setVisible(true);
    }

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new GUI().initUI();
            }
        });
    }

}

One option would be that you use modal dialogs. Once you open the modal dialog, the code after that will execute only after you close it. Once closed, you can call a getter method on the dialog class from the outside, in order to get the entered values.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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