Event Handling in JavaFX - java

I am confused all about the subject of event handling.
I am trying to implement a game. I already wrote the game logic separately, and the GUI (on JavaFX).
Below is some sample code; What can I do so that the updateScoresLabel() method will run whenever the setScore(...) method is executed?
public class MyGameLogic
{
private int scores=0;
public void setScore(int scores)
{
this.scores=scores;
}
public int getScore()
{
return scores;
}
}
public class JustAGUIExample
{
Label scoresLabel;
MyGameLogic gameLogic;
public void updateScoresLabel()
{
this.scoresLabel=gameLogic.getScore();
}
}

Use Binding Instead of Event Handlers
You don't need an event handler to accomplish a label update when a model change occurs.
You can bind your label property to your model property, then when you change the model, the label will update automatically.
Adapting the code from your question to use binding.
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.control.Label;
public class MyGameLogic {
private IntegerProperty scores = new SimpleIntegerProperty(0);
public void setScore(int scores) {
this.scores.set(scores);
}
public int getScore() {
return scores.get();
}
public IntegerProperty scoreProperty() {
return scores;
}
}
class JustAGUIExample {
private Label scoresLabel;
private MyGameLogic gameLogic;
public JustAGUIExample() {
scoresLabel.textProperty().bind(
gameLogic.scoreProperty().asString()
);
}
}
There are extensive examples of this kind of binding strategy in this JavaFX tic-tac-toe game example.
For more complex logic use a ChangeListener
Let's say you also wanted to play a victory sound when the score changes, you could use something like this:
class JustAGUIExample {
private Label scoresLabel;
private MyGameLogic gameLogic;
private AudioClip levelUpAudio = new AudioClip("levelup.mp3");
public JustAGUIExample() {
scoresLabel.textProperty().bind(
gameLogic.scoreProperty().asString()
);
gameLogic.scoreProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
levelUpAudio.play();
}
});
}
}
So a ChangeListener is kind of like an event listener for property changes. But I only say kind of because in JavaFX events are there own separate things and are usually reserved for GUI system events like mouse clicks, window resizing notification, touchpad swipes, etc.
Using Java 8 the syntax is nicer:
gameLogic.scoreProperty().addListener((observable, oldValue, newValue) ->
levelUpAudio.play()
);
Tutorial on Event Handling in Java
Even though you don't really need event handling for the sample from your question, you can read up on the Oracle JavaFX event handling tutorial to find out what events really are and how they work.
My Thoughts On Swing Based Suggestions
As you are writing a JavaFX program please disregard any suggestions which relate to event handling in Swing. Instead, learn to do this stuff in a JavaFX way or you will just confuse yourself.

For a GUI to run an event, the class must implement ActionListener. From this the method actionPerformed must be added to that class.
Here is a sample implementation of that
//Run, help, and about are all buttons on this frame
public void actionPerformed(ActionEvent e){
if(e.getSource() == run){ //Check if the event was the run button being pressed
//Run the "run" program
}else if(e.getSource() == about){ //Check if the event was the about button being pressed
//Open welcome
}else if(e.getSource() == help){ //Check if the event was the help button being pressed
//Have the help screen appear
}
}

Related

How do I build a JMenu dynamically (and not merely run-time creation)?

To be clear, the several similar-appearing entries here DO NOT actually talk about building a menu dynamically since all their object name choices and such are already in their code as fixed strings already written in the source code; all they're doing is waiting until run-time to create their statically designed menu items. Here are two entries I found like that: One and Two. The concerns there merely had to do with the trivial (but vital) task of refreshing the display, NOT with anything like actual dynamic creation of content.
What I want to do, in sharp contrast, is to truly add dynamically: I want the user to be able to choose to add items to a sub-menu that they can then later select and have take action within the application.
Let's take the case of simply adding an integer value to a menu and then being able to select it later, similar to what can easily be done with a combo-box but instead done with a menu.
The problem isn't the syntax pertaining to defining, for example, a MenuListener that will point to a method that knows how to act, that's not the problem. Rather, I just don't know enough about the dynamic NAMING SPACE, and how to "de-reference" a String, for example, as an object name. Bluntly, how do I dynamically name my new objects that I didn't anticipate creating (not in kind but in number)? IOW, how do I take a cleverly constructed string that actually contains code I want run and then ask Java to run it? What's the Java syntax for that? Maybe the problem can be reduced to just object names; Say, the name comes as a string I can construct; how do use that in my JMenuItem declaration? ...I know how to do this in BASH, but how is this done in Java?
(I'm hoping I don't have to create it as a file, compile it, and somehow attach the class file(s) to my running program and then run it - DAMN that would be cumbersome!)
Thanks.
If I understand your overall intent, then I would recommend starting with the Actions API which be used to create independent units of work which are independent of how they are displayed.
This allows you to define re-usable (or in your case, dynamic) operations, which can be executed via menus, toolbars, buttons and even key bindings out of the box.
Because setting up a Action can be a little tedious, I might consider using a builder pattern, but you don't have to, you can build them manually if you wish ;)
public class ActionBuilder {
private ActioBuilderAction action;
public ActionBuilder() {
action = new ActionBuilder.ActioBuilderAction();
}
public ActionBuilder toolTip(String text) {
action.putValue(Action.SHORT_DESCRIPTION, text);
return this;
}
public ActionBuilder command(String text) {
action.putValue(Action.ACTION_COMMAND_KEY, text);
return this;
}
public ActionBuilder mnemonic(int key) {
action.putValue(Action.MNEMONIC_KEY, key);
return this;
}
public ActionBuilder displayedMnemonicIndex(int index) {
action.putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, index);
return this;
}
public ActionBuilder text(String text) {
action.putValue(Action.NAME, text);
return this;
}
public ActionBuilder smallIcon(Icon icon) {
action.putValue(Action.SMALL_ICON, icon);
return this;
}
public ActionBuilder largeIcon(Icon icon) {
action.putValue(Action.LARGE_ICON_KEY, icon);
return this;
}
public ActionBuilder acceleratorKey(KeyStroke ks) {
action.putValue(Action.ACCELERATOR_KEY, ks);
return this;
}
public ActionBuilder actionListener(ActionListener listener) {
action.setListener(listener);
}
public Action build() {
return action;
}
public class ActioBuilderAction extends AbstractAction {
private ActionListener listener;
public void setListener(ActionListener listener) {
this.listener = listener;
}
#Override
public void actionPerformed(ActionEvent e) {
if (listener != null) {
listener.actionPerformed(e);
}
}
}
}
Then, you could simply build a new menu something like...
Action action = new ActionBuilder().text("Super awesome command").actionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Super aweseom comand GO!");
}
}).build();
JMenuItem mi = new JMenuItem(action);
Now, I imagine, you might have a "command executor" class of some kind, which would. physically execute the command. I'd create a bridging class which implemented ActionListener and when it's called, would then execute the specified command
public class CommandListener implements ActionListener {
private String command;
public CommandListener(String command) {
this.command = command;
}
#Override
public void actionPerformed(ActionEvent e) {
CommandExecutor executor = new CommandExecutor();
executor.execute(command)
}
}
This could then be used in place of the ActionListener in the first example...
Action action = new ActionBuilder().text(commandName).actionListener(new CommandListener(command)).build();
As an overall idea

GWT Updating Main Content using EventBus and ActivityMapper - Can GWT help me to do this cleaner?

I would like to know
Am I doing things (the following) too complicated?
Is there a better way to update the main content of an activity that allows me to bookmark the event calendar of a store via URL like #MainPlace:eventCalendar?storeId=<id>?
I'm having this ActivityMapper here
public class AppActivityMapper implements ActivityMapper {
private ClientFactory clientFactory;
private MainActivity mainActivity;
// ..
#Override
public Activity getActivity(Place place) {
if (place instanceof LoginPlace) {
return new LoginActivity((LoginPlace) place, clientFactory);
} else if (place instanceof MainPlace) {
if(this.mainActivity == null) {
this.mainActivity = new MainActivity((MainPlace) place, clientFactory);
} else {
this.mainActivity.updateMainContent(((MainPlace) place).getMainContentToken());
}
return this.mainActivity;
}
return null;
}
}
and a MainActivity that controls my MainView that is just a menu ond the left side and the main content on the right side.
I want to decouple my views like in Best Practices for Architecting GWT App which is why I'm trying to control the main content by using events that get fired as something gets clicked in my MenuView.
Therefore I am initializing some event handlers in my MainActivity that react to clicks on the buttons in my menu to delegate the update to the MainView.
public class MainActivity extends AbstractActivity implements MainView.MainPresenter {
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
this.mainView = this.clientFactory.getMainView();
this.mainView.setPresenter(this);
this.mainView.initialize();
this.eventBus = eventBus;
this.eventBus.addHandler(HomeClickedEvent.TYPE, new HomeClickedHandler() {
#Override
public void onHomeClicked(HomeClickedEvent event) {
goTo(new MainPlace("home"));
}
});
this.eventBus.addHandler(EventCalendarClickedEvent.TYPE, new EventCalendarClickedHandler() {
#Override
public void onEventCalendarClicked(EventCalendarClickedEvent eventCalendarClickedEvent) {
goTo(new MainPlace("eventCalendar?storeId=" + eventCalendarClickedEvent.getStoreId()));
}
});
panel.setWidget(this.mainView.asWidget());
}
#Override
public void goTo(Place place) {
this.clientFactory.getPlaceController().goTo(place);
}
#Override
public void updateMainContent(String currentMainContentToken) {
this.mainView.updateMainContent(currentMainContentToken);
}
}
this event gets fired by MenuPresenter.clickedEventCalendar() that reacts to a click on the corresponding menu entry of the MenuView:
public class MenuPresenter implements MenuView.MenuPresenter {
// ..
#Override
public void clickedEventCalendar(Long storeId) {
this.eventBus.fireEvent(new EventCalendarClickedEvent(storeId));
}
}
One of the things I really don't like is this where I append parameters to the token e.g. to display the event calendar of a store given by storeId:
#Override
public void onEventCalendarClicked(EventCalendarClickedEvent eventCalendarClickedEvent) {
goTo(new MainPlace("eventCalendar?storeId=" + eventCalendarClickedEvent.getStoreId()));
}
is there a cleaner solution for a problem like this in GWT? I don't like the fact that I'd have to parse that string in my actual event calendar. Am I using the ActivityMapper wrong or is there simply no other way to do this?
This question should really be split into several separate ones, but that's maybe something to keep in mind for the future. If you're asking one thing then it's easier to answer thoroughly and others can find the answer easier too.
Anyway, I can see a few improvements:
use EventBinder to get rid a bit of the cruft when handling and creating new events.
if you just want to let the presenter know that a button was pressed on in the view (associated with that presenter) sending a custom event over the event bus is a bit of an overkill. Depending on your needs you can expose the button in your view's interface:
public interface Display {
HasClickHandlers getButton();
}
And then just register the ClickHandler in your presenter.
Or, if you need to do something view- and presenter- related on the click, register the ClickHandler in your view and call the presenter:
// In MainView:
#UiHandler("button")
void handleClick(ClickEvent event) {
// Do some stuff with view,
// like hide a panel or change colour
panel.setVisible(false);
// Let the presenter know that a click event has been fired
presenter.onEventCalendarClicked();
}
you're right - creating MainPlace like you are proposing is wrong. You are creating the token too soon - that's what the tokenizer associated with the place is for. You should create MainPlace by passing just the storeId to the constructor - why should MainPresenter (or any other class using this place) should know how to create the token? MainPlace should look more like this:
public class MainPlace extends Place {
private final Long storeId;
public MainPlace(Long storeId) {
this.storeId = storeId;
}
public Long getStoreId() {
return storeId;
}
public static class Tokenizer implements PlaceTokenizer<MainPlace> {
#Override
public MainPlace getPlace(String token) {
return new MainPlace(Long.valueOf(token));
}
#Override
public String getToken(MainPlace place) {
return "eventCalendar?storeId=" + place.getStoreId();
}
}
}
Now, it's the Tokenizer's responisibily to create and parse the token. Just remember to register it on your PlaceHistoryMapper.

button.onActionProperty().addListener is not working in javafx

I am new in Java and I am trying Java events but I am absolutely lost. I know events from C# where is no problem with it and they works perfectly, but Java is different universe . I tried to find something on internet but I can't figured it out long time so I am here.
I have one object and I need trigger some action. When this action is triggered I need to call not only one event handler, but more of them from different objects.
For example I just use Button class.
There are two ways how to do that:
One way is to use button.setOnAction method. But this is not working because when I call this method second time (from another object) I just replace one event handler by another. You can see what I mean in code in method initEventsUselessWay().
Second way is to use button.onActionProperty().addListener. But this is not working at all. You can see in method initEventsNeededWay().
So, why button.onActionProperty().addListeneris not working?
And is there any way how to do this in Javafx?
Finally I will not use Button class, but something like MyClass and I need to implement this here. But if this is not working on Button class it will not work on MyClas neither.
Thank you very much for advice.
package sample;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class JavaEventsTest1589 extends Application {
private Button btnDemo1;
private Button btnDemo2;
#Override
public void start(Stage primaryStage) {
// panel
Pane rootPane = new Pane();
// scene
Scene scene = new Scene(rootPane, 300, 250);
primaryStage.setTitle("events demo");
primaryStage.setScene(scene);
// button 1
btnDemo1 = new Button();
rootPane.getChildren().add(btnDemo1);
btnDemo1.setText("Execute Demo 1");
btnDemo1.setLayoutX(50);
btnDemo1.setLayoutY(10);
// button 2
btnDemo2 = new Button();
rootPane.getChildren().add(btnDemo2);
btnDemo2.setText("Execute Demo 2");
btnDemo2.setLayoutX(50);
btnDemo2.setLayoutY(50);
initEventsUselessWay();
initEventsNeededWay();
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private void initEventsUselessWay() {
btnDemo1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
runDemoPrimaryReaction();
}
});
btnDemo1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
runDemoSecondaryReaction();
}
});
}
private void initEventsNeededWay() {
btnDemo2.onActionProperty().addListener(new ChangeListener<EventHandler<ActionEvent>>() {
#Override
public void changed(ObservableValue<? extends EventHandler<ActionEvent>> observableValue, EventHandler<ActionEvent> actionEventEventHandler, EventHandler<ActionEvent> actionEventEventHandler2) {
runDemoThisINeed_No1();
}
});
btnDemo2.onActionProperty().addListener(new ChangeListener<EventHandler<ActionEvent>>() {
#Override
public void changed(ObservableValue<? extends EventHandler<ActionEvent>> observableValue, EventHandler<ActionEvent> actionEventEventHandler, EventHandler<ActionEvent> actionEventEventHandler2) {
runDemoThisINeed_No2();
}
});
}
private void runDemoPrimaryReaction() {
System.out.println("useless way - primary reaction");
}
private void runDemoSecondaryReaction() {
System.out.println("useless way - secondary reaction");
}
private void runDemoThisINeed_No1() {
System.out.println("not working way - No1");
}
private void runDemoThisINeed_No2() {
System.out.println("not working way - No2");
}
}
Use addEventHandler:
btnDemo1.addEventHandler(ActionEvent.ACTION, new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
runDemoPrimaryReaction();
}
});
btnDemo1.addEventHandler(ActionEvent.ACTION, new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
runDemoSecondaryReaction();
}
});
I recommend using Java 8, in which case you can write
btnDemo1.addEventHandler(ActionEvent.ACTION, event -> runDemoPrimaryReaction());
btnDemo1.addEventHandler(ActionEvent.ACTION, event -> runDemoSecondaryReaction());
The setOnAction(...) method is a "convenience" method. The way it works is that the Button maintains an ObjectProperty<EventHandler<ActionEvent>>. If you set the value of this property (to an EventHandler<ActionEvent>), that event handler will automatically be added to the event handlers for the button. If you set it a second time, since it's just a property, it will replace the existing event handler. So you can use this for a slightly quicker approach in the case you only have one handler. (It also plays nicely with FXML.)
onActionProperty().addListener(...) is a different thing entirely: it listens for changes in the property itself. So if you register a listener in this way, then call setOnAction(...), the listener you registered with the property will be invoked when you call setOnAction (not when the button is pressed).
Have a look at the tutorial, particularly the first section "Processing events" and the second "Working with convenience methods". The second section makes it clear when setOnAction is actually doing.
You need to think of an 'action' as a property of the Button rather than an event.
A Button can only have one action at a time, hence you are setting it to one value and then changing it to another which means only the code in the second handler will execute.
The action property is implemented using one of the JavaFX property classes which provides built in property change notification. When you call button.onActionProperty().addListener(... you are adding a listener that will be invoked when the action is changed.
Try calling the addListener(... code before calling setOnAction(... and it should be clear what is happening.
If you want to think of this in terms of C# then
button.setOnAction(.. is like a property e.g. button.Action = ...
button.onActionProperty().addListener(... is like an event e.g. button.ActionChanged += ...
If you are new to JavaFX and need some help with their particular implementation of properties and events I can suggest the following resources:
Creating JavaFX Properties
Creating read-only properties in JavaFX
JavaFX: Handling events
JavaFX: Using properties and binding
Update:
After reading your comments I realise this isn't really JavaFX specific but about events in general. Events are just an implementation of the observer pattern (even in C#).
A basic implementation might look something like this although there is little point in writing this yourself as JavaFX does include a built in way registering listeners for events as demonstrated by James_D.
public interface MyListener {
invoke();
}
class MyButton extends Button {
List<MyListener> listeners = new ArrayList<MyListener>();
public void addListener(MyListener toAdd) {
listeners.add(toAdd);
}
private void invoke() {
for (HelloListener l : listeners)
l.invoke();
}
this.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
this.invoke();
}
});
}
Based on the OP's comment
So I exprect that when I click on button "Execute Demo 1" in console I will see in console two rows: 'useless way - primary reaction useless way - secondary reaction'
you need to call both the methods in the same event handler as following:
btnDemo1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
runDemoPrimaryReaction();
runDemoSecondaryReaction();
}
});

Java - Doing large scale GUI projects

To get right directly to my question.
How do you do large scale GUI projects. I have not done any larger GUI projects in java so far but what i am working on now grew pretty fast and pretty big and now i am stuck whit a huge pile of code that is really annoying and messy.
Since i come from field of web development i am used to MVC frameworks so i have 3 packages in my projects Model where i keep classes that interact whit files or db, Views where i keep my classes for Forms or GUI and Controller package where i keep the majority of my logic.
I have been told to separate my logic as well keep actions in one class and listeners in another class but i have no idea how to link all that up.
So far i only have 1 Controller class where i execute all the methods regarding whats happening on the GUI once its invoked.
package pft.controller;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;
import javax.swing.JComboBox;
import javax.swing.JTree;
import java.awt.event.*;
import javax.swing.JProgressBar;
import pft.view.Invoke_GUI;
import pft.model.Events;
import pft.model.Parse;
public class Tower_Controller {
public Tower_Controller() {
}
//Global variables
String isSelected = null;
int hasModules = 0;
int cap = 0;
int cpu = 0;
int shield = 0;
int armor = 0;
public void setName(String name){
this.isSelected = name;
}
public String getName(){
return this.isSelected;
}
public void setCap(int cap){
this.cap = cap;
}
public int getCap(){
return this.cap;
}
public void setCpu(int cpu){
this.cpu = cpu;
}
public int getCpu(){
return this.cpu;
}
public void setShield(int shield){
this.shield = shield;
}
public int getShield(){
return this.shield;
}
public void setArmor(int armor){
this.armor = armor;
}
public int getArmor(){
return this.armor;
}
public void invoke() throws IOException {
Invoke_GUI runnable = new Invoke_GUI();
final JLabel tower_name = runnable.tower_name;
final JComboBox tower_select = runnable.tower_select;
final JTree module_browser = runnable.module_browser;
final JTree selected_modules = runnable.selected_modules;
final JProgressBar cap_bar = runnable.cap_bar;
final JProgressBar cpu_bar = runnable.cpu_bar;
final JLabel em_res = runnable.em;
final JLabel th_res = runnable.thermic;
final JLabel ki_res = runnable.kinetic;
final JLabel ex_res = runnable.explosive;
setTowerName(tower_name, tower_select);
removeTower(tower_name);
runnable.setVisible(true);
}
public void removeTower(final JLabel tower_name) {
tower_name.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
if (hasModules == 1 & isSelected != null) {
Events evt = new Events();
evt.towerHasModules();
} else if (isSelected == null) {
} else {
tower_name.setText("No Control Tower selected");
isSelected = null;
}
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
}
public void updateVariables(String name) throws IOException{
Parse tower = new Parse();
String data[] = tower.towerData(name);
Integer x = Integer.valueOf(data[1]).intValue();
setCap(x);
}
public void setTowerName(final JLabel tower_name, final JComboBox tower_select) {
tower_select.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (isSelected != null) {
Events evt = new Events();
evt.towerSelected(isSelected);
} else {
tower_name.setText(tower_select.getSelectedItem().toString());
setName(tower_name.toString());
try {
updateVariables(tower_name.toString());
} catch (IOException ex) {
Logger.getLogger(Tower_Controller.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
}
}
There are a lot of tutorials and examples how to do small usually single class Java GUI but no tutorials or examples on how to do projects that are bit larger than a single class.
Thanks in advance for all the help and
advice.
Here is my advice for Swing development in general. It does discuss the importance of using Controllers to bridge the needs of the view and the interace of the model.
GUI guidelines for swing
Last Swing project I did I designed a MVC framework that used Spring for defining the model of the program and Controllers, then used annotations in the Controller to wire up events dispatched by the view onto methods in the controller. The view had access to the event dispatcher which was an event bus, and events sent over the bus called methods on the Controller through the annotations. This allowed any Controller to respond to events from the View. So as a Controller got too large it was super simple to refactor each set of methods into another Controller, and the view or model didn't have to change.
The beauty of the event bus was it could be shared with the model as well so the model could dispatch asynchronous events the Controller could register for as well. It looked something like:
public class SomeController {
private AuthenticationModel authenticationModel;
private LoginService loginService;
private MyApp view;
#Listener( event = "login" )
public void login( LoginEvent event ) {
view.showWaitDialog();
loginService.login( event.getUserName(), event.getPassword() )
.onResult( new Callback<User>() {
public void onResult( User user ) {
authenticationModel.setUser( user );
view.hideWaitDialog();
view.showStartScreen(user);
}
});
}
}
Once this framework was in place it was amazing how fast we could get things done. And it held up pretty well as we added features. I've done my fair share of large Swing projects (3 to date), and this architecture made a huge difference.
The easiest way to scale a GUI is to make everything loosely coupled. Events (Swing's and your own) are the best way to do this. Unless a class is directly creating or showing a GUI element, it shouldn't know or care about anything else in the UI.
The Controller should continue doing what it's supposed to do - firing events in response to other events. But, these events should be application level events defined by the needs of your app. The Controller shouldn't directly manipulate GUI elements. Instead, you should create components (maybe just subclasses of JWhatever) that register themselves with the Controller as interested in events.
For example, create an TowerEventListener interface with a nameChanged() function. The Controller also has a changeTowerName() function, which when called, updates the model (a Tower class) then calls nameChanged() on all registered TowerEventListeners.
Then create a TowerRenamer class that, for example, subclasses JDialog (i.e. a popup window) that includes a text box and and OK button along with a reference to the Controller. When the user clicks OK, Controller.changeTowerName() is called. Other parts of your GUI that register as TowerEventListeners will receive the event and update as needed (maybe by updating a JLabel on the UI).
Note, if your needs are simple enough, you can just use PropertyChangeEvents and not worry about a whole event interface structure. PropertyChangeSupport can be used by the Controller to fire event notifications.
In addition to the great advice already given, I would recommend reading some of what Trygve Reenskaug has written and/or recorded on his MVC page. He was there during the development of this architectural style in the late 70's. His two page technical report entitled Models - Views - Controllers from December of 1979 presents the most concise description of the model, view, and controller.
Of particular note, views are both observers and manipulators of the model. The controller is primarily concerned with arranging (wiring) the views and translating user input into interactions with the model. Several of the MVC frameworks out there have the controller relaying data from the model to the view - this is simply wrong. A paper from earlier in 1979 included the concept of an editor as a composite of related views. The editor was discarded; its functionality was moved into both the controller and the view.
Another article that is good at describing how to apply this guideline is Burbeck's How to use Model-View-Controller. It is written with Smalltalk in mind so it might not translate to Java easily, but it is a good description of how to apply the guideline.
I think that the most important thing to consider is that the original MVC style was created for user interfaces that included more than one view (representation) of the same model. This really works well for user interfaces but does not translate exceptionally well to the web service world. Using MVC for a GUI lets you really see and understand the power of this style.

Passing Swing classes (references) on network?

I want to know that is it possible to send major Swing classes event/actionlisteners, Events, components via RMI.
Possible scenario: If one client press the button or move the slider every client's button or slider move etc same for other swing elements.
I am expecting the answer in the context of RMI and swing MVC architecture, i want to call the swing component's models e.g ButtonModel and want to send swing ActionEvent on wire and register PropertyChangeListener/PropertyChangeSupport as remote objects for getting updates at client site.
typical examples :
the server should call this method for each client, when ever some change occur in model
public void propertyChange(PropertyChangeEvent evt) {
for (AbstractViewPanel view: registeredViews) {
view.modelPropertyChange(evt);
}
}
in case of an event on one client, each client actionPerformed should be called from server:
#Override
public void actionPerformed(ActionEvent e) {
}
is it feasible? if not then why? where i could face the problems, i mean which classes are transferable (serialized) and which are not...
EDIT: here you see i m invoking Java Swing defaultbuttonmodel remotely, the only thing left when some of it's property or method change the other client's get updates, best would be following swing propertychangelistener if someone can just help me, realizing this, it would be great:
public class RemoteButtonModel extends UnicastRemoteObject implements Model {
private ButtonModel model = new DefaultButtonModel() ;
protected myModel() throws RemoteException {
super();
}
#Override
public void setEnabled(boolean b) throws RemoteException {
if (isEnabled())
model.setEnabled(false);
else{
model.setEnabled(true);
}
}
#Override
public boolean isEnabled() throws RemoteException {
return model.isEnabled();
}
}
I think it would be more efficient to send across something like a "scroll message" or "button pressed" message, utilizing the command pattern. This would allow different clients to act correctly with different implementations.
Edits:
the way I do it in my client/server applications (which is easily adapted to this peer-to-peer architecture you're doing) is with something like this (copy and pasted from my production code, mind you.)
abstract public class UserRequest implements Serializable {
public final String username;
private transient ServersideThread thread;
protected UserRequest(String username) {
this.username = username;
this.thread = null;
}
abstract public EngineMessage engineCallback(GenericEngine engine);
public void setThread(ServersideThread thread) {
if(this.thread == null) {
this.thread = thread;
return;
}
throw new IllegalStateException("Cannot set thread when already set:" + thread.getName());
}
public ServersideThread getThread() {
return this.thread;
}
}
So, with this approach, I would do something like...
public class SliderMoveNotification extends UserRequest {
// need some way to say what slider moved
public final int sliderId;
public final int slideDistance;
public SliderMoveNotification(String username) {
super(username);
sliderId = 0;
sliderDistance = 0;
throw new UnsupportedOperationException("Must supply a slider and a distance");
}
public SliderMoveNotification(String username, int sliderID, int slideDistance) {
super(username);
this.sliderId = sliderId;
this.slideDistance = slideDistance;
}
public EngineMessage engineCallback(GenericEngine engine) {
if(engine instanceof WindowEngine) {
WindowEngine manager = (WindowEngine)engine;
manager.slideWindow(sliderId,slideDistance);
// you wouldn't need engine messages like I do in my client/server
// relationship, but the idea stands.
}
}
}
The Javadoc for every Swing class says that it should not be serialized.
More probably you should be transmitting the associated Model classes.
And event listening via RMI is an anti-pattern. Too much traffic, too many points of failure.

Categories