1

I am creating a Desktop Java Application (just for fun and expanding knowledge) using NetBeans. I have a Spinner (and a Button) in a Panel and I need to detect when the Spinner loses focus. I have tried several things but nothing will fire either focusGained or focusLost for the Spinner. How can I get this to work?

What I have tried:

ATTEMPT 1: I tried using "implements FocusListener" at the class level, then Overridding the focusGained and focusLost events as follows.

    @Override
    public void focusGained(FocusEvent e) {
        String name = e.getComponent().getClass().getName();
        System.out.println("Focus gained : " + name);
    }

    @Override
    public void focusLost(FocusEvent e) {
        String name = e.getComponent().getClass().getName();
        System.out.println("Focus lost : " + name);
    }

I added listeners as follows. I only had the commented out line at first. This worked perfectly for the button but the Spinner never fired either event when clicking on it, changing it, and then clicking off of it to the button. So then I tried commenting out that line and added the line below to try and use the underlying TextField. This again did not work.

        jButton1.addFocusListener(this);
        //jSpinner1.addFocusListener(this);
((JSpinner.DefaultEditor)jSpinner1.getEditor()).getTextField().addFocusListener(this);   

ATTEMPT 2: Next I removed the Class level FocusListener and tried building a new one inline as follows. Again I tried first building it directly on the Spinner and when that didn't work, I tried building it on the underlying TextField. Again neither event ever fired.

    //jSpinner1.addFocusListener(new FocusListener() {
    ((JSpinner.DefaultEditor)jSpinner1.getEditor()).getTextField().addFocusListener(new FocusListener() {
      @Override
      public void focusGained(FocusEvent e) {
        String name = e.getComponent().getClass().getName();          
        System.out.println("Focus gained : " + name);
      };
      @Override
      public void focusLost(FocusEvent e) {
        String name = e.getComponent().getClass().getName();          
        System.out.println("Focus lost : " + name);
      };
      });

ATTEMPT 3: Finally I tried building a separate focusAdapter as follows. Again I tried attaching it first directly to the Spinner and then to the underlying TextField and again neither event ever fired.

        FocusAdapter focusAdapter = new FocusAdapter()
        {
            public void focusGained(FocusEvent e)
            {
                String name = e.getComponent().getClass().getName();          
                System.out.println("Focus gained : " + name);
            }
            
            public void focusLost(FocusEvent e) {
                String name = e.getComponent().getClass().getName();          
                System.out.println("Focus lost : " + name);
            };            
        };        
        
        //jSpinner1.addFocusListener(focusAdapter);
        ((JSpinner.DefaultEditor)jSpinner1.getEditor()).getTextField().addFocusListener(focusAdapter);

I will add that I have tried using both AdoptOpenJDK jdk-11.0.11.9-hotspot and Oracle OpenJDK jdk-16.0.1 just to see if that might make a difference. Same results with both.

I should also add that I have made sure that both the "focusable" property and the "requestFocusEnabled" property of the Spinner are set to "true".

I am at a complete loss. All 3 methods should have worked and do with other non-Spinner components. Is focus just broken with Spinners or am I missing something?

Maverickz
  • 81
  • 1
  • 6
  • Ah, for the want of something simple...have a look at a this [example](https://stackoverflow.com/questions/15328185/make-jspinner-select-text-when-focused/15328399#15328399) and the prey that some else has a much simpler solution, I do like the comment `// and eventually throwing a hissy fit ... ` basically says it all really – MadProgrammer Jun 17 '21 at 22:32
  • 1
    *"I need to detect when the Spinner loses focus."* .. ***Why?*** See [What is the XY problem?](http://meta.stackexchange.com/q/66377) – Andrew Thompson Jun 17 '21 at 23:29
  • @MadProgrammer thanks for the pointer. I am reading through it, to see how I can best implement it. I see one comment on that page that says it doesn't work if you change the model (which I am, to use the Date model). So I'll have to see what I can get to happen. I may end up having to use mouse click listener on everything that is not the spinner. LOL – Maverickz Jun 17 '21 at 23:38
  • *"I may end up having to use mouse click listener on everything that is not the spinner."* What about when the user tabs out of the spinner? – Andrew Thompson Jun 17 '21 at 23:41
  • @Andrew Thompson To answer your question. I have a label that displays a time. The Spinner is invisible and sits on top of it. When the label is clicked the Spinner is displayed and allows me to change the time. When it loses focus (or press Enter), the spinner needs to be hidden again. Essentially I am recreating something I wrote years ago in .Net just as a way to learn building a UI in Java. Learning to build Applications/Tools that can run in Linux is the last thing I need to ditch Windows. – Maverickz Jun 17 '21 at 23:44
  • 3
    @Maverickz When the model is changed, the editor is rebuilt, so, you need to remove the listener before you set the model and reapply it after it's been changed – MadProgrammer Jun 18 '21 at 00:05
  • @MadProgrammer THAT WAS THE KEY! I moved the jSpinner1.setEditor line to come before the addFocusListener line and now both the code from my Second and Third attempts are now working. Now to decide which way I prefer. Thanks a ton. – Maverickz Jun 18 '21 at 00:24

1 Answers1

1

Thanks to @MadProgrammer's tip I have the answer. I had the DateEditor and the setEditor lines below where I set the Listener. This was clearing the listener I had just set. I moved those two lines to before I set the Listener and now both sets of the following code work for me.

Using a FocusAdapter:

        JSpinner.DateEditor de = new JSpinner.DateEditor(jSpinner1, "h:mm:ss a");
        jSpinner1.setEditor(de);        
        
        FocusAdapter focusAdapter = new FocusAdapter()
        {
            public void focusGained(final FocusEvent e)
            {
                String name = e.getComponent().getClass().getName();          
                System.out.println("Focus gained : " + name);
            }
            
            public void focusLost(final FocusEvent e) {
                String name = e.getComponent().getClass().getName();          
                System.out.println("Focus lost : " + name);
            };            
        };        
        
        ((JSpinner.DefaultEditor)jSpinner1.getEditor()).getTextField().addFocusListener(focusAdapter);   

Using an inline Listener:

    ((JSpinner.DefaultEditor)jSpinner1.getEditor()).getTextField().addFocusListener(new FocusListener() {
      @Override
      public void focusGained(FocusEvent e) {
        String name = e.getComponent().getClass().getName();          
        System.out.println("Focus gained : " + name);
      };
      @Override
      public void focusLost(FocusEvent e) {
        String name = e.getComponent().getClass().getName();          
        System.out.println("Focus lost : " + name);
      };
      });

I suspect but haven't tried, that the first method I tried (in my original question above) would also work if I also moved those two lines to before I attached the Listener.

Maverickz
  • 81
  • 1
  • 6