Search code examples
javaswingjbutton

Is there a way I can loop the checking of the state of Frame.state, w/out looping the code in the check?


I want to be able to continuously check the value of Frame.state, without continuously looping the code within the checks. I want to be able to either check the value or check when a button has been pressed, and re-print the values in the Jpane accordingly.

I have taken the checks and put it within the ActionListener() for button "next" in Frame, but I do not want to have to do this unless absolutely necessary. I've tried putting the if checks within a while loop, the result was a continuous repeat of everything within each check, based on the button state, i.e.: The code within the first check "if (Frame.state == 1){array.setRandom(100);array.print();}" would continuously reprint the array with random numbers, instead of performing the action once, and waiting for the button to be pressed again.

I do not believe ArrayObject2d is important for the helping or solution of this problem and have chose to leave it out, if that code is necessary please let me know.

public class MainClass {
public static void main(String[] args) {
        @SuppressWarnings("unused")
        Frame frame = new Frame();
        ArrayObject2d array = new ArrayObject2d(10, 5);

        if (Frame.state == 1) {
            array.setRandom(100);
            array.print();
        } else if (Frame.state == 2) {
            array.setAll(99999);
            array.print();
        } else if (Frame.state == 3) {
            array.numberAll();
            array.print();
        } else if (Frame.state == 4) {
            array.showIndex();
        }

    }

Below code is within another class , under the constructor which fully creates the frame with all components



//assume all imports have been written
public class Frame extends JFrame {
    public Frame() {
        super();
        setDefaultCloseOperation(3);
        setBackground(new Color(245, 252, 255));
        setResizable(false);
        setSize(1280, 720);
        setVisible(true);
        setLocationRelativeTo(null);
        JButton next = new JButton();

        next.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (state > 3) {
                    state = 1;
                } else {
                    state++;
                }       
            }
        });
    }   
}

I expect the checks to be continuously run, but instead the code within the checks were run continuously.


Solution

  • No, don't loop and continually poll your object for its state as this is very inefficient. Rather, you should make state a "bound" field, and allow other code to attach property change listeners to the object so that the GUI object can notify any and all listeners if the state changes.

    So:

    • Make the state field a private instance field of your Frame class.
    • Give the class a public static final String STATE = "state"; constant
    • Create a getter and setter method for state, public int getState(), public void setState(int state)
    • Within the setState(...) method create two local int variables oldValue and newValue, and pass the respective state values (the parameter and the this value) into these variables.
    • Your Frame has property change support. So use it to notify any attached listeners that state has change by calling firePropertyChange(STATE, oldValue, newValue);

    For example:

    Main class that creates both ArrayObject2d and your Frame object (here named Frame2 to avoid confusion with java.awt.Frame)

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.GridBagLayout;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class MainClass {
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> {
                final Frame2 frame = new Frame2();
                frame.setVisible(true);
                final ArrayObject2d array = new ArrayObject2d(10, 5);
    
                frame.addPropertyChangeListener(Frame2.FRAME_STATE, propChngEvent -> {
                    if (frame.getFrameState() == 1) {
                        array.setRandom(100);
                        array.print();
                    } else if (frame.getFrameState() == 2) {
                        array.setAll(99999);
                        array.print();
                    } else if (frame.getFrameState() == 3) {
                        array.numberAll();
                        array.print();
                    } else if (frame.getFrameState() == 4) {
                        array.showIndex();
                    }
                });
            });
        }
    }
    

    The Frame2 class is below. Note that the state field has been re-named frameState to avoid confusion with an internal Swing field of the same name.

    @SuppressWarnings("serial")
    class Frame2 extends JFrame {
        public static final String FRAME_STATE = "frame state";
        private static final Dimension PANEL_SIZE = new Dimension(640, 400);
        private JLabel frameStateLabel = new JLabel("1");
        private int frameState = 1;    
    
        public Frame2() {
            super();
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setLocationRelativeTo(null);
            JButton next = new JButton("Next");
            JPanel panel = new JPanel(new GridBagLayout());
            panel.setPreferredSize(PANEL_SIZE);
            panel.setBackground(new Color(245, 252, 255));
            panel.add(next);
            panel.add(Box.createHorizontalStrut(15));
            panel.add(new JLabel("Frame State: "));
            panel.add(frameStateLabel);
    
            next.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (frameState > 3) {
                        setFrameState(1);  // ***Always*** change frameState by the setter method
                    } else {
                        setFrameState(frameState + 1);  // ** Ditto **
                    }       
                }
            });
    
            add(panel);
            pack();
            setResizable(false);
            setLocationRelativeTo(null);
        }   
    
        public int getFrameState() {
            return frameState;
        }
    
        public void setFrameState(int frameState) {
            int oldValue = this.frameState;
            int newValue = frameState;
            this.frameState = frameState;
            firePropertyChange(FRAME_STATE, oldValue, newValue);
            frameStateLabel.setText("" + frameState);
        }
    }
    

    This is a mock-up class for your ArrayObject2d class, since I don't have the actual code. It will output what method has been called:

    class ArrayObject2d  {
    
        public ArrayObject2d(int rows, int columns) {
            // TODO finish
        }
    
        public void showIndex() {
            System.out.println("show index");
        }
    
        public void numberAll() {
            System.out.println("number all");
        }
    
        public void setAll(int i) {
            System.out.println("set all: " + i);
        }
    
        public void print() {
            System.out.println("print");
        }
    
        public void setRandom(int i) {
            System.out.println("set random: " + i);
        }
    
    }