1

Following my previous post here , I wrote a listener :

@Override
public void keyTyped(KeyEvent keyEvent) 
{
    PolygonFiller polyFiller = new PolygonFiller();
    char key = keyEvent.getKeyChar();

    final boolean runForever = true;

    switch(key)
    {
        /**
         *  Fill the polygons 
         */
        case FILL_POLYGON:      
        {
            if (greenLightForFilling == true)
            {

                while (runForever)
                {
                    fillPolygon(polyFiller);
                    KeyListener listener = new KeyListener()
                    {
                        public void keyPressed(KeyEvent keyEvent) 
                        {
                            char keyOther = keyEvent.getKeyChar();
                            if (keyOther == 'F' || keyOther == 'f')
                                // can't use break;
                                runForever = false;
                        }

                        @Override
                        public void keyReleased(KeyEvent arg0) {}

                        @Override
                        public void keyTyped(KeyEvent arg0) {}
                    };
                }
            }
            break;

        }  // end FILL_POLYGON


        case FILL_POLYGON_LOWERCASE:
        {
            if (greenLightForFilling == true)
            {
                fillPolygon(polyFiller);
            }
            break;              

        }

        /**
         *  save all polygons in a .scn file
         */
        case SAVE_POLYGONS :         
        {
            if (greenLightForFilling == true)
            {
                saveWorkspace();
            } 
            break;  
        }   // end SAVE_POLYGONS



        case SAVE_POLYGONS_LOWERCASE:
        {
            if (greenLightForFilling == true)
            {
                saveWorkspace();
            } 
            break;  
        }

        /**
         *  Delete everything & load all polygons from .scn file
         */
        case LOAD_POLYGONS:      
        {
            loadWorkspace();
            break;
        }   

        case LOAD_POLYGONS_LOWERCASE:
        {
            loadWorkspace();
            break;
        }

        default: break;  
    } // end switch


} 

The goal :

  1. break from the loop , when I get a second f or F (I already got one f/F when I entered to loop) .

  2. Do I need to attach the second listener to something ?

But I can't change the runForever to false , since it's written outside .

Even if I change it to final , I can't change its value to false either .

Any way around this ?

Note : I already have a key-listener for entering the switch-case!!

EDIT:

// Hot-keys hit by the user - used for keyboard listening
private static final char FILL_POLYGON = 'F';
private static final char SAVE_POLYGONS = 'S';
private static final char LOAD_POLYGONS = 'L';
private static final char FILL_POLYGON_LOWERCASE = 'f';
private static final char SAVE_POLYGONS_LOWERCASE = 's';
private static final char LOAD_POLYGONS_LOWERCASE = 'l';


    @Override
    public void keyTyped(KeyEvent keyEvent) 
    {
        PolygonFiller polyFiller = new PolygonFiller();
        char key = keyEvent.getKeyChar();

        final Settable runForever = new Settable();

        switch(key)
        {
            /**
             *  Fill the polygons 
             */
            case FILL_POLYGON:      
            {
                if (greenLightForFilling == true)
                {

                        KeyListener listener = new KeyListener()
                        {
                            public void keyPressed(KeyEvent keyEvent) 
                            {
                                char keyOther = keyEvent.getKeyChar();
                                if (keyOther == 'F' || keyOther == 'f')
                                    runForever.set(false);
                            }

                            @Override
                            public void keyReleased(KeyEvent arg0) {}

                            @Override
                            public void keyTyped(KeyEvent arg0) {}
                        };

                        this.addKeyListener(listener);


                        while (runForever.get())
                        {
                            fillPolygon(polyFiller);
                        }
                }
                break;

            }  // end FILL_POLYGON

...
}
Community
  • 1
  • 1
JAN
  • 21,236
  • 66
  • 181
  • 318

3 Answers3

4

Final variables cannot be changed, but if they represent a mutable object, you can change the content of that object.

For example, if you make a Settable class, and use its instance instead of boolean, you could do this:

class Settable {
    private boolean flag;
    public boolean get() {return flag;}
    public boolean set(boolean val) { flag = val; }
}

Now, use a final instance of Settable in your code; use set(...) instead of the assignment, and get() instead of a direct access:

final Settable runForever = new Settable();
runForever.set(true);
...
while (runForever.get()) {
    ...
    if (keyOther == 'F' || keyOther == 'f') runForever.set(false);
    ...
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    You could use AtomicBoolean for this, instead of making a new class. – JeroenWarmerdam Apr 02 '13 at 12:51
  • 1
    @JeroenWarmerdam I would be reluctant to use an `AtomicBoolean` or an array with a single `boolean` value because they were meant for something really different. Using `AtomicBoolean` hints at concurrency to the readers of your code, where there is none. That may mislead your readers, which to me is worse than writing an extra five lines of code for a new class. – Sergey Kalinichenko Apr 02 '13 at 12:53
  • Why do you feel that this is an incorrect use of AtomicBoolean? – JeroenWarmerdam Apr 02 '13 at 12:55
  • @dasblinkenlight: Great , thanks . One more thing , how can I activate that listener now ? since when I hit `F` / `f` again , the listener doesn't respond , so I guess it's not activated . – JAN Apr 02 '13 at 12:59
  • @JeroenWarmerdam I am not suggesting that it is incorrect or even suboptimal, only that the readers may get confused. When I see `Atomic`, I think "concurrency"; the OP's code is asynchronous, but it is not concurrent. I believe that avoiding reader's confusion is nearly as important as the code correctness. However, using `AtomicBoolean` or even `boolean runForever[1]` is technically correct. – Sergey Kalinichenko Apr 02 '13 at 13:00
  • @ron You need to add your `listener` to the same input element that takes keyboard interaction (text area?) You should add it *outside* the loop, and remove it as soon as the loop is over. – Sergey Kalinichenko Apr 02 '13 at 13:06
  • @dasblinkenlight: I have (please see the edited post) but the second `F` doesn't respond . Did I leave anything behind ? – JAN Apr 02 '13 at 13:17
  • @ron You forgot to set `runForever` to `true` before the loop. You also need to call `this.removeKeyListener(listener)` after the loop. – Sergey Kalinichenko Apr 02 '13 at 13:29
  • @dasblinkenlight: I did change it to true , inside the class , but the modifications that you mentioned did work... it still gets stuck and won't respond to any `f`/`F` . thanks . – JAN Apr 02 '13 at 13:33
  • @dasblinkenlight Thanks for the explanation. I thought your thought proces might be as worthwhile to OP as the answer itself was. – JeroenWarmerdam Apr 02 '13 at 13:34
  • @dasblinkenlight: No , `private static final char FILL_POLYGON = 'F';` – JAN Apr 02 '13 at 13:42
  • @ron Do you press an uppercase `F` or a lowercase `f` the second time around? I think you would need a redesign there, because both listeners consume `F`. More importantly, the "busy loop" on the UI thread prevents any of your listeners from responding - ever. That's the biggest problem with your current design: you should offload the filling of the rectangle onto a different thread. – Sergey Kalinichenko Apr 02 '13 at 13:48
  • @dasblinkenlight good answer for Java Essential Classes, please read comments in OPs previous post, and to see my answer here, by default this answer could be down_voted :-) – mKorbel Apr 02 '13 at 14:10
  • @mKorbel Well, the answer solves the declared issue at hand of communicating between an inner class and its enclosing method. Although the bigger problem of running a busy loop in a UI thread remains (see my last comment) it is not practical to rephrase the original question to ask for a solution to that other problem: I think that offloading the busy loop onto a separate thread deserves a question of its own. – Sergey Kalinichenko Apr 02 '13 at 14:22
  • @dasblinkenlight not attack to your post here as I'm mentioned good answer for Essential Classes :-), nothing solved his idea, OP will returns here with a few side effects caused from KeyListener and Swing JComponents, deleted comment by (@Guillaume Polet) talking about that simple and very clear – mKorbel Apr 02 '13 at 14:58
3
  • please did you read comment by @kleopatra in your previous post, not good idea to ignore that

  • not never, not wrong direction, use KeyBindings and with Swing Action instead of KeyListener

  • accepted answer by @dasblinkenlight solved nothing in Swing, Swing and Java2D, use KeyBindings and with Swing Action,

for example

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

public class FullScreen {

    private static final long serialVersionUID = 1L;
    private JButton button = new JButton("Close Meeee");
    private JPanel myPanel = new JPanel();
    private JFrame frame = new JFrame();

    public FullScreen() {
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        myPanel = new JPanel() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(400, 300);
            }
        };
        myPanel.setFocusable(true);
        myPanel.add(button);
        frame.add(myPanel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //frame.setUndecorated(true);
        frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
                KeyStroke.getKeyStroke("ENTER"), "clickENTER");
        frame.getRootPane().getActionMap().put("clickENTER", updateBol());
        frame.pack();
        frame.setVisible(true);
        updateCol().setEnabled(false);
        updateDol().setEnabled(false);
    }

    private Action updateBol() {
        return new AbstractAction("updateBol") {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("updateCol is " + updateBol().isEnabled());
                System.out.println("updateBol is " + updateCol().isEnabled());
                System.out.println("updateDol is " + updateDol().isEnabled());
                updateCol().actionPerformed(e);
                updateDol().actionPerformed(e);
            }
        };
    }

    private Action updateCol() {
        return new AbstractAction("updateCol") {
            private static final long serialVersionUID = 1L;
            private Boolean _enabled = false;

            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("updateCol is " + updateCol().isEnabled());
                if (updateCol().isEnabled()) {
                } else {
                }
            }

            public void setEnabled(Boolean bol) {
                _enabled = bol;
            }

            @Override
            public boolean isEnabled() {
                return _enabled;
            }
        };
    }

    private Action updateDol() {
        return new AbstractAction("updateDol") {
            private static final long serialVersionUID = 1L;
            private Boolean _enabled = false;

            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("updateDol is " + updateDol().isEnabled());
                if (updateCol().isEnabled()) {
                } else {
                }
            }

            public void setEnabled(Boolean bol) {
                _enabled = bol;
            }

            @Override
            public boolean isEnabled() {
                return _enabled;
            }
        };
    }

    public static void main(String[] args) {
        Runnable doRun = new Runnable() {
            @Override
            public void run() {
                FullScreen fullScreen = new FullScreen();
            }
        };
        SwingUtilities.invokeLater(doRun);
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
1

You can make final array of boolean of size 1 to store your flag. Even if array is final, it's elements are not, so you can modify array's elements from anonymous classes.

infthi
  • 537
  • 1
  • 5
  • 19