简体   繁体   中英

Java Swing Jbutton accessing a pressed button from another class?

Hi I have this problem in java, trying to create UI using swing. I came across a problem that I cannot figure out, and am in need of some help.

What I want to happen is when the button is clicked, that it passes it to the object ui created in the method run().

public class MainTest{
    public static void main(String[] args){
        MainTest test = new MainTest();
        test.run();
    }

    public void run(){
        UITest ui = UITest();
        ui.start();

        while (true){
            if (ui.getClicked()) break;
        }

        System.out.println("Working this far");
}


public class UITest{
    private JFrame frame;
    private boolean clicked = false;

    public void start(){
        EventQueue.invokeLater(new JframeCreator());
    }

    private class JframeCreator implements Runnable{
        public void run(){
            createUI();
            frame.setVisible(true);
        }
    }

    private void createUI(){
        frame = new JFrame();
        frame.setResizable(false);
        frame.setSize(353, 264);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(null);

        JButton btnNewButton = new JButton("test");
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                clicked = true;
            }
        });
        btnNewButton.setBounds(118, 150, 89, 32);
        frame.getContentPane().add(btnNewButton);

    }

    public boolean getClicked(){
        return clicked;
    }
}       

To me this looks like it should work, but it does not get to the code that outputs "work this far". I click the button and nothing happens

it does work though, once I add in an output value. And now when I click the button it runs.

public void run(){
    //previous code
    while(true){
        if (ui.getClicked()) break;
        System.out.println("Not working"); //Added
    }

    System.out.println("working this far");
}

I just can not seem to figure out why it works this way and what I am missing.

Hopefully I made this clear enough.

One way is to use an observer design pattern and listen for state changes in the GUI. One way to do this is to notify listeners by firing a Swing component's innate PropertyChangeSupport by calling the firePropertyChange(...) method. Then any and all listeners that have registered with that component via addPropertyChangeListener(...) can listen for this event. For example:

import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.*;

public class MainTest{
   public static void main(String[] args){
      final MtNonGui nonGui = new MtNonGui();

      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            MtGuiPanel guiPanel = new MtGuiPanel();
            JFrame testFrame = new JFrame("Test");
            testFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            testFrame.add(guiPanel);
            testFrame.pack();
            testFrame.setLocationByPlatform(true);
            testFrame.setVisible(true);

            guiPanel.addPropertyChangeListener(MtGuiPanel.PRESS_ME_ACTION, new PropertyChangeListener() {

               @Override
               public void propertyChange(PropertyChangeEvent evt) {
                  nonGui.buttonPressed();
               }
            });
         }
      });
   }

}

class MtNonGui {

   public void buttonPressed() {
      System.out.println("Button Pressed");
   }

}

class MtGuiPanel extends JPanel {
   public static final String PRESS_ME_ACTION = "press me action";
   private JButton button = new JButton(new PressMeAction("Press Me"));

   public MtGuiPanel() {
      add(button);
   }

   private class PressMeAction extends AbstractAction {
      public PressMeAction(String name) {
         super(name);
         int mnemonic = (int) name.charAt(0);
         putValue(MNEMONIC_KEY, mnemonic);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         MtGuiPanel.this.firePropertyChange(PRESS_ME_ACTION, false, true);
      }
   }
}

Disadvantages:

  1. it's a lot of code for a simple notification

Advantages:

  1. It's all decoupled -- the GUI is completely ignorant about who is listening to it, and so is the non-GUI object.
  2. No need for an infinite loop to poll the state of the GUI.
  3. It scales nicely, so as your program gets bigger and more complex, this shouldn't break things.

The clicking of the button happens in a different thread than the one that is running the loop. The change in the private variable is not visible to the loop thread and therefore it continues to loop.

You should make the clicked variable volatile . This will ensure that a change in it will be visible to other threads that are interested in it.

The printing command may cause the memory model to allow the new value to be visible to that thread, but of course, that is not the correct way to do it.

Of course, having a tight loop waiting on a boolean variable is not all that advisable, either.

Use a modal dialog of some kind instead of a JFrame and the check the state of the UI when control returns to your code.

A modal dialog will block at the point it is made visible.

public class MainTest{
    public static void main(String[] args){
        MainTest test = new MainTest();
        test.run();
    }

    public void run(){
        EventQueue.invokeLater(new Runnable(){
            public void run() {
                UITest ui = UITest();
                boolean wasClicked = ui.start();

                System.out.println("Working this far");
            }
     }
}


public class UITest{
    private JDialog frame;
    private boolean clicked = false;

    public boolean start(){
        createUI();
        frame.setVisible(true);
        return clicked;
    }

    private void createUI(){
        frame = new JDialog();
        frame.setResizable(false);
        frame.setSize(353, 264);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // Really, really bad idea
        frame.getContentPane().setLayout(null);

        JButton btnNewButton = new JButton("test");
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                clicked = true;
                dispose();
            }
        });
        btnNewButton.setBounds(118, 150, 89, 32);
        frame.getContentPane().add(btnNewButton);

    }

    public boolean getClicked(){
        return clicked;
    }
}       

See How to use dialogs for more details

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