I've been working on a project while implementing the MVP design-pattern (at least to my knowledge) where i just have my one controller/presenter class which handles all of the actions from UI point of view using inner classes
public class PresenterClass {
public PresenterClass(ViewClass view) {
view.getButtonComponent().addActionListener(new ButtonListener());
view.getMenuItem().addActionListener(new MenuItemListener());
}
class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//Do some action with the button
}
}
class MenuItemListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//Do some action with the menu item
}
}
}
Worth it to know that each new class listener is created for any new component that needs it and it isn't just a generic listener for all Buttons, so you can end up with a lot of inner classes which is what is happening in my project. My project isn't massive but it's growing and while i don't think it would be big enough to be completely unmanageable i have thought about how i could handle it if i needed to handle hundreds of different components? So this leads to the question of:
How do you handle large scalability while implementing an MVC or MVP design pattern?
Related
I need to write a Java Swing application that will run and act about the same on Linux, Windows and Mac. And for virtually all Widgets I create, I need to add some special handling, including, everything needs to be able to pop up a menu when asked. Even widgets like labels and buttons.
I'm able to catch menu requests by adding:
MouseListener m = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e)
{
if (e.isPopupTrigger())
{
onMenuSummons(); //do my thing
}
}
};
addMouseListener(m);
to the constructor on my extended widget.
But I'm concerned that this is a Linux-only solution. Do I only need to check isPopupEvent() in MousePressed? What about MouseClicked? I could hook both to be sure, but do I run a risk that some platform someday would have isPopupEvent() be true in both functions for the same mouse action?
Ideally MouseAdapter would spare me the question by offering an overridable popupRequest(MouseEvent e), but it doesn't. So what is the always right, works everywhere on all platforms and always will forever solution?
Also, some of the widgets I'm extending may have their own popup menus; I need to suppress those and implement my own. How do I make sure only my own menu is displayed? TIA.
Read the section from the Swing tutorial on Bringing up a Popup Menu for the basic of using menus and popup menus.
To write a MouseListener the basic code is:
class PopupListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e) {
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(),
e.getX(), e.getY());
}
}
}
some of the widgets I'm extending may have their own popup menus; I need to suppress those and implement my own.
Also note the the example from the tutorial is older. The newer approach for adding a popup menu to a component is to use:
component.setComponentPopupMenu(....);
Not sure, but since this method only allows a single popup it may replace the default popup?
I have a simple Controller object that that implements Observer (i.e. it is the observer) which listens for events from a Mouse object that extends Observable (i.e. it is the observee).
So there can be multiple events in a Mouse object e.g. left click, scroll up, etc.
Now the Controller class implements the required method which is called every time any of the above events happen:
#Override
public void update(Observable o, Object arg)
{
// Handle mouse event
}
So it is the arg parameter that I am having trouble deciding how to approach (this case I am working on is simple enough such that the o parameter is not a concern). What is a good way to distinguish between different mouse events without using typeof?
I.e. what parameter should I feed in the notifyObservers(Object arg) method in the Mouse observee class?
Reading the comments it seems that you are using SWT -- SWT already implements event handlers via observer/observable callbacks.
What you are trying to do here is implement another layer of observer pattern on top of the already implemented observer pattern by SWT, which doesn't make much sense. But if you really wanted to do it for practice you might want to make several inner private classes to handle different events, e.g:
/**
* Controller.java
*/
import java.util.Observable;
import java.util.Observer;
public class Controller
{
/**
* The controller's inner classes are to be notified of mouse events.
*/
public Controller(Mouse mouse)
{
mouse.addObserver(new LeftClickObserver());
mouse.addObserver(new ScrollUpObserver());
}
private class LeftClickObserver implements Observer
{
#Override
public void update(Observable o, Object arg)
{
// TODO Handle left clicks
}
// Other left click logic here
// ...
}
private class ScrollUpObserver implements Observer
{
#Override
public void update(Observable o, Object arg)
{
// TODO Handle scroll up
}
// Other scroll up logic here
// ...
}
}
Then you can call the notifyObservers(Object arg) method in the appropriate event in the Mouse class to trigger a callback to the Controller.
However as mentioned, you should instead make use of the existing SWT libraries to handle events, then separate the implementation of the event handling in the controller, and away from the view (as you are trying to do already). Here's a simple example of how to do that (assuming you have a Controller initialized somewhere in the Mouse class, and the appropriate methods in the Controller class):
final Button button = new Button(shell, SWT.PUSH);
button.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent event) {
controller.handleWidgetSelectedEvent(event); // controller decides what to do...
}
public void widgetDefaultSelected(SelectionEvent event) {
controller.handleWidgetDefaultSelectedEvent(event); // controller decides what to do...
}
});
Note that there are some versions of the MVC pattern where the View does much of the handling of the events, and/or the model; so really, it depends on your project. Probably handling the event at the view straight away is the easiest strategy.
Please help me understanding the difference between adding the action listener to JComponent in following two approaches.
First Method: Implementing actionListener to my class and adding the common actionPerformed method which choose selection based on the events
class Test implements ActionListener
{
JButton jbutton = null;
public Test(){
jbutton = new JButton();
jbutton.addActionListener(this);
}
public void actionPerformed(ActionEvent e){
//Perform operation here;
}
}
Second Method: Defining the action listener for individual JComponent.
JButton jbutton = new JButton();
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
//Perform operation here
}
});
What is the difference between these two approach and which one is more cleaner and maintainable approach and if there is any efficiency benefit involved ?
I'd go with the first approach if:
The action is fired via different events. For example, you have an action that changes the language of the GUI from English to Arabic (where you need to re-arrange the components to lay from right to left) and that action can be fired via some key bindings like (Alt + R) and via a JMenuItem, and maybe via some buttons.
Several actions have the same base code. For example, a calculator application where each math operation button would fire the same action and based on the action command you can determine the operation from inside actionPerformd(). They share the GUI updates.
and I'd go with the second approach if:
The action is tied to only one event and you want to write it on the fly.
What I wouldn't do is something similar to this:
public class MainFrame extends JFrame implements ActionListener
but I would write:
public class CustomListener implements ActionListener
Also see:
Best practice for implementing ActionListener
best practice for gui and actionlistener
ActionListener best practices
Nested class vs implements ActionListener
Organize Code for GUI and ActionListener in Java
define button actions in inner class Vs define button actions in public class in swing
What is better to use: Action vs ActionListener?
I'm trying to listen for a window close event on the parent JFrame of a JPanel. In the WindowClosing event I'd like to de-register a listener to a different component.
Unfortunately the only code I can gaurantee to have run is the constructor for the panel. What this means is that the panel itself doesn't have an ancestor window yet, so simply calling SwingUtilities.getWindowAncestor doesn't work. So what I do is register a hierarchy listener, and in the hierarchyChanged event look for SHOWING_CHANGED event. When that even fires, now I can look for the window ancestor of the panel.
So basically I have the following:
class ShapeControlPanel extends JPanel{
public ShapeControlPanel(){
final ShapeControlPanel me = this;
me.addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
if((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) == HierarchyEvent.SHOWING_CHANGED){
SwingUtilities.getWindowAncestor(me).addWindowListener(new WindowListener() {
/* Snipped some empty handlers */
#Override
public void windowClosing(WindowEvent e) {
/* Finally get to remove the handler. */
me.getApparent().removeLocationSelectionListener(me.GUID(), me);
}
});
}
}
});
}
}
Is this sane? Is there a more reasonable way of getting a handle on the frame closing event?
It's not the ugliest thing I've seen (I wouldn't even say it's all that bad), but you have to ask yourself: why does your panel really need to know when the window is closed? It seems to be an odd coupling that would best be removed.
I don't know enough about your context and what you are truly trying to accomplish to suggest an alternative right now. But if a panel needs to know about the container in which it resides, there is probably some bad design with harmful coupling.
When developing Swing applications, I've typically defined a delegate interface for each UI component for action callbacks. For example, if there is a class, MyDialog, which contains a button, MyButton, then the ActionListener for MyButton will call MyDialog.Delegate.OnMyButtonClick(Event e). The UI component then becomes "dumb" and requires a controller to handle events as well as update the component itself.
I thought that by using the Swing Application Framework's #Actions, I could get around creating delegate interfaces and implementations by defining #Action methods in implementation classes, letting the ApplicationContext figure out what to call. Apparently, that is not the case, as I don't see any clear way of adding those classes into the ApplicationContext, nor do I see any examples out there of doing such a thing.
Has anyone managed to use SAF in this manner so that there is a clean separation between UI and UI action code?
I've discovered a good way to keep the UI separate from the behavior using #Actions.
First, create a UI Component, say a JPanel with a button and then give it a public method that can be used to set the action of the Button:
class CustomJPanel extends JPanel {
private JButton myButton;
public CustomJPanel() {
initializeComponents();
}
public void initializeComponents() {
myButton = new JButton();
}
public void setButtonAction(javax.swing.Action action)
{
myButton.setAction(action);
}
}
Next, create an Action class that will provide the logic for that button:
class CustomJPanelActions {
#Action
public void doSomething()
{
JOptionPane.showMessageDialog(null,"You pressed me!");
}
}
Finally, setup the application controller and during setup, assign the appropriate action to the appropriate UI:
class MyApp extends SingleFrameApplication {
private JFrame mainFrame;
private JLabel label;
#Override
protected void startup() {
getMainFrame().setTitle("BasicSingleFrameApp");
CustomJPanel panel = new CustomJPanel();
panel.setButtonAction(getContext().getActionMap(new CustomJPanelActions()).get("doSomething");
show(panel);
}
public static void main(String[] args) {
Application.launch(BasicFrameworkApp.class, args);
}
}
In this way, the UI is logically separated from the control (i.e. Action) and can be tested on its own. The controller can make any decisions it needs to in order to determine what Action set to use and which specific action to assign to the UI controls. That is, one can create a Test Action Set and a Live Action Set, etc.
This method of using SAF has worked rather well for me.
The SAF javadoc has some information on how to do this sort of thing in the doc for ActionManager#getActionMap