2

I'm trying to build a simple Java Swing application using the MVC architecture pattern. What I've done is create the user interface components (as private) in my views, and have public methods that return the components. These methods are then called by the controllers, through which I can write methods for event/action listeners. Below is a sample example:

View:

private JButton btnAdd;

    public JButton getBtnAdd(){
        return btnAdd;
    }

Control:

myGuiFrame gui = new myGuiFrame();


        //on add button clicked
    gui.getBtnAdd().addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            //calls to model
        }
    });

Is this implementation correct?

If so, then I'm having a problem with FocusListeners. When I create a FocusListener in my view, the focusLost and focusGained methods are created within the view.

private FocusListener l;

someComponent.addFocusListener(l);

        l = new FocusListener() {

            @Override
            public void focusLost(FocusEvent e) {
                // TODO Auto-generated method stub
            }

            @Override
            public void focusGained(FocusEvent e) {
                // TODO Auto-generated method stub

            }
        };

I want all the event handlers to be in my controllers. My question is ... is there a way I can call/declare the focusLost and focusGained methods from my controller? I tried to define the FocusListener as public so that I can define it in my controller:

view:

public FocusListener l;
public someComponentType someComponent;

controller:

gui.l = new FocusListener() {
    @Override
    public void focusLost(FocusEvent e) {
        // TODO Auto-generated method stub

}

@Override
public void focusGained(FocusEvent e) {
    // TODO Auto-generated method stub
    gui.someComponent.addFocusListener(gui.l);

    }   

};

This however does not work.

Is it possible to handle FocusEvents from the controller?

EDIT:

Gosh, my bad. Didn't quite understand what Robin was all about. I was too fixated on having the FocusListener explicitly defined somewhere. A simple:

    gui.getTextFieldEmployeeCode().addFocusListener(new FocusListener() {

        @Override
        public void focusLost(FocusEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void focusGained(FocusEvent e) {
            // TODO Auto-generated method stub
            System.out.println("YES!!!");
        }
    });

in the controller would work just fine in the manner I planned to do it, though I quite like how nIcE cOw's gone about it. Just out of curiosity, is there a standard or widely accepted manner of implementing MVC on Swing Apps?

greatkalu
  • 433
  • 1
  • 8
  • 21
  • 2
    For better help sooner, include an [sscce](http://www.sscce.org). – user1329572 Apr 20 '12 at 12:48
  • 1
    AFAIK, `button.addActionListener(...);` is a part of View, not Controller, now what you passed as an argument to that function, is the controller class, like `button.addActionListener(new ButtonController());` , now inside your `ButtonController` Class which implements `FocusListener` resides these two methods `focusGained(...)/focusLost(...)` – nIcE cOw Apr 20 '12 at 12:52
  • I do not see the difference between your `ActionListener` and your `FocusListener`. You create and add the `ActionListener` in your controller by using the available getter. Why not use the same approach for the `FocusListener` ? – Robin Apr 20 '12 at 12:53
  • IMO You are designating too much to the controller - away from the view – ControlAltDel Apr 20 '12 at 12:53
  • Will use sscce next. @nIcE cOw, are you suggesting that I declare all my even handlers in the same .java file as the one containing my GUI components, then calling methods of our controller from within them? – greatkalu Apr 20 '12 at 13:08
  • @Robin If I were to use that approach, i.e View `public FocusListener getFocusListenerL() { return l; }` Controller (Don't know what to do): `gui.getFocusListenerL().focusGained(new FocusEvent(source, id))` Eclipse is asking for a source and an id. Also, where will I be able to define which components are added to the focusListener? – greatkalu Apr 20 '12 at 13:11
  • @user1295681 : Here watch my answer, is this what you trying to do :-), no no, rather pass your `View object` to your controller, have a look. – nIcE cOw Apr 20 '12 at 13:12
  • Here a wonderful example given by trashgod related to [MVC](http://stackoverflow.com/a/3072979/1057230). Based on this [Java Document about MVC in Swing](http://java.sun.com/products/jfc/tsc/articles/architecture/) and here one wonderful example by @HovercraftFullOfEels regarding [MVC Architecture](http://stackoverflow.com/questions/6087436/how-to-work-with-swing-with-multiple-classes/6088659#6088659) – nIcE cOw Apr 20 '12 at 14:36

1 Answers1

6

As far as I understood, the way you doing is this. Better still, I prefer the Anonymous Classes, they respect the concept of Encapsulation. Here try your hands on this code, see what you can grasp with this :

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

public class View
{
    private JButton focusButton;
    private JButton spareButton;

    private void createAndDisplayGUI()
    {
        JFrame frame = new JFrame("VIEW");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JPanel contentPane = new JPanel();
        contentPane.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));

        focusButton = new JButton("GAINED/LOST");
        focusButton.addFocusListener(new ButtonController(this));

        spareButton = new JButton("SPARE");
        spareButton.setOpaque(true);
        spareButton.addActionListener(new ButtonController(this));
        spareButton.addFocusListener(new ButtonController(this));

        contentPane.add(focusButton);
        contentPane.add(spareButton);

        frame.setContentPane(contentPane);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public JButton getFocusButton()
    {
        return focusButton;
    }

    public JButton getSpareButton() 
    {
        return spareButton;
    }

    public static void main(String... args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new View().createAndDisplayGUI();
            }
        });
    }
}

class ButtonController implements FocusListener, ActionListener
{
    private View view;
    private JButton focusButton;
    private JButton spareButton;

    public ButtonController(View v)
    {
        view = v;
        focusButton = view.getFocusButton();
        spareButton = view.getSpareButton();
    }

    public void actionPerformed(ActionEvent ae)
    {
        JButton button = (JButton) ae.getSource();

        if (button == spareButton)
        {
            spareButton.setBackground(Color.BLUE);
            focusButton.setEnabled(true);
        }
    }

    public void focusGained(FocusEvent fe)
    {
        JButton button = (JButton) fe.getSource();

        if (button == focusButton)
        {
            focusButton.setEnabled(true);
        }
        else if (button == spareButton)
        {
            spareButton.setBackground(Color.WHITE);
        }
    }

    public void focusLost(FocusEvent fe)
    {
        JButton button = (JButton) fe.getSource();

        if (button == focusButton)
        {
            focusButton.setEnabled(false);
        }
        else if (button == spareButton)
        {
            spareButton.setBackground(Color.DARK_GRAY.darker());
        }
    }
}

Another approach, if you feel irritated by getters/setters and sending references around, is to define one inner class as follows (A Simple workaround for the previous example) :

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

public class View
{
    private JButton focusButton;
    private JButton spareButton;

    private void createAndDisplayGUI()
    {
        JFrame frame = new JFrame("VIEW");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JPanel contentPane = new JPanel();
        contentPane.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));

        focusButton = new JButton("GAINED/LOST");
        focusButton.addFocusListener(new ButtonController());

        spareButton = new JButton("SPARE");
        spareButton.setOpaque(true);
        spareButton.addActionListener(new ButtonController());
        spareButton.addFocusListener(new ButtonController());

        contentPane.add(focusButton);
        contentPane.add(spareButton);

        frame.setContentPane(contentPane);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private class ButtonController implements FocusListener, ActionListener
    {

        public void actionPerformed(ActionEvent ae)
        {
            JButton button = (JButton) ae.getSource();

            if (button == spareButton)
            {
                spareButton.setBackground(Color.BLUE);
                focusButton.setEnabled(true);
            }
        }

        public void focusGained(FocusEvent fe)
        {
            JButton button = (JButton) fe.getSource();

            if (button == focusButton)
                focusButton.setEnabled(true);
            else if (button == spareButton)
                spareButton.setBackground(Color.WHITE);
        }

        public void focusLost(FocusEvent fe)
        {
            JButton button = (JButton) fe.getSource();

            if (button == focusButton)
                focusButton.setEnabled(false);
            else if (button == spareButton)
                spareButton.setBackground(Color.DARK_GRAY.darker());
        }
    }

    public static void main(String... args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new View().createAndDisplayGUI();
            }
        });
    }
}
nIcE cOw
  • 24,468
  • 7
  • 50
  • 143
  • Still going through it, and will test. Damn that was fast. Many thanks! Of course it doesn't help that I'm using WindowBuilder (Eclipse plugin) to build my GUI. May be complicating things more than I bargained for. – greatkalu Apr 20 '12 at 13:25
  • @user1295681 : You are MOST Welcome and KEEP SMILING :-) – nIcE cOw Apr 20 '12 at 14:28
  • @ nIcE cOw: Please see edited question for what I've done to modify my existing code. Thanks once again :) – greatkalu Apr 20 '12 at 14:31
  • @user1295681 : Those `Answers/Document` referred in the comment, can guide you well in that, regarding the best approach. – nIcE cOw Apr 20 '12 at 14:47