How do JButtons in advanced applications work? - java

How do buttons in software written in Java work?
For example the above screenshot: when the user clicks different buttons, different algorithms are run on user-inputted data (it's a data analysis application) and the output is displayed. Just getting started writing Java GUI's though, it all seems like magic to me -- is there one ActionListener for every pane? Does it listen for different ActionCommands of the different buttons and execute the algorithm right within the actionPerformed() method (it seems a little nonintuitive to me to execute an algorithm in a method independent of data...i.e. the button doesn't know what data it's dealing with?). So far, all the action listener tutorials I've read online have merely printed something when the button is pressed...
What's the general structure for connecting button, actionlisteners, and actual actions performed in the background?
Thanks in advance.

The usual way is to have one action listener per button. The Statistics panel has access (via one of its fields), to the data it needs to read and modify). So, the handling of the first button in this panel could look like:
private void initButtonListeners() {
this.averageDegreeButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
computeAverageDegree();
}
}
// other buttons...
}
And the computeAverageDegree() method could look like
private void computeAverageDegree() {
double result = this.statistics.computeAverageDegree();
this.averageDegreeLabel.setText(formatDoubleToString(result));
}

My personal preference is to do almost nothing in the UI, but move it all to the model/controller side (not sure what the best name is as it is seldom pure MVC).
I think that everything you do in the UI should be doable through the API as well. Benefits are easier testing, redesign of the UI is possible without messing up your logic, easy to perform the heavy work on background threads, ... .
A good read describing this is the Humble Dialog article. Not really Swing specific, but applicable to all sort of UI's.
To answer your questions:
is there one ActionListener for every pane?
No, typically you have an Action (or ActionListener) for each button. I prefer to use Action instances as they are far more reusable then the typical anonymous ActionListener (and easier to test as well)
Does it listen for different ActionCommands of the different buttons and execute the algorithm right within the actionPerformed() method
Certainly not. Doing heavy calculations in that method will block the Swing UI thread (the Event Dispatch Thread), which results in a non-responsive UI while the calculations are ongoing. Showing progress becomes also impossible. Calculations are typically done on a worker thread, launched when your Action is triggered (for example using a SwingWorker). This is explained in the Concurrency in Swing tutorial.
it seems a little nonintuitive to me to execute an algorithm in a method independent of data...i.e. the button doesn't know what data it's dealing with?
The button should not know about the data. The data is typically stored in the model. The UI is only displaying it, but does not contain it (unless it is input just provided by the user). The button should just know what to call on the model. The model does whatever it has to do and fires an event. The UI picks up that event and updates itself.
At least, that is how Swing is designed (for example JTable and its TableModel). I so no good reason to not follow that model when making your own Swing UI's

Related

Advantages of InteractionDialog over Dialog?

I was recommended to use InteractionDialog rather than Dialog, but I'm failing to see the advantages. What I can see is a problem. What I need is letting the user enter a PIN or whatever and wait for their answer. This is needed both on the EDT thread (the user choose to save the PIN) and on other threads (a web page requires the PIN for login).
With Dialog,
I can call it from the EDT thread and it works nice.
When on a different thread, I can be trivially adapted by a one-liner in the callee (see getFromGui in my linked question).
With InteractionDialog,
I can use it easily from other threads via some simple wait/notifyAll magic.
I can't use it from the EDT thread, except via callbacks like okBtn.addActionListener(...), which is verbose and ugly.
So I am confused and asking:
What do I gain from the InteractionDialog?
Is there a simple way how to use it uniformly no matter what thread I am on?
There are two separate things here:
Modality
How it works
A dialog can be modal or non-modal but it isn't interactive like an InteractionDialog. The modal dialog blocks the EDT internally using InvokeAndBlock so the current thread stops until there's a response from the dialog. This is convenient but has some edge case issues. E.g. the event that launched the dialog might trigger other events that would happen after the dialog was dismissed and cause odd behavior.
But that's not the big thing in modality. Modality effectively means the form behind you "doesn't exist". Everything that matters is the content of the dialog and until that is finished we don't care about the form behind. This core idea meant that a dialog effectively derives form and as such it behaves exactly like showing another form effectively disabling the current form. What you see behind the dialog is a drawing of the previous form, not the actual form.
Text fields can pose a problem. Because the way the dialog is positioned (effectively padded into place within its form using margin) the UI can't be scrolled as text field requires when the virtual keyboard rises. Since people use dialogs in such scenarios we try to workaround most of these problems but sometimes it's very hard e.g. if the dialog has a lot of top margin, the virtual keyboard is open and covering it. Or if the user rotates the screen at which point the margin positioning the dialog becomes invalid.
Note that in InteractionDialog Some of these issues such as the margin to position also apply.
Now InteractionDialog is a completely different beast that sprung out of a completely different use case. What if we want a dialog such as a "color palette that floats on top of the ui?
We can move it from one place to another but still interact with the underlying form. That's the core use case for InteractionDialog. As such modality is no longer something we need so it was never baked into InteractionDialog although it technically could have been (but it doesn't make sense to the core use case).
It's implemented as a Container placed into the layered pane of the current form so the form around it is real. Because the form is "live" layout works better and the removal of modality makes some edge cases related to editing slightly better. There are still some inherent problems with dialog positioning and rotation though. It also allows you to click outside of the dialog while input is ongoing which might be a desirable/undesirable effect for your use case.
Overall I try to use dialogs only for very simple cases and avoid input when possible. If I use input I never use more than one field (e.g. no-username and password fields) so I won't need to scroll. These things work badly for native UIs as well e.g. with the virtual keyboard obscuring the submit button etc. Since those behaviors are very hard to get right for all resolution/virtual keyboard scenarios.
Based on the answer from Shai, I wrote a form working as the base class for most of my dialogs. Basically, it shows the content from the subclass and adds the "OK" and "Cancel" buttons.
There's a method for use from the EDT thread like
public void showAndThen(BooleanConsumer consumer) {
assert CN.isEdt();
...
okBtn.addActionListener(a -> {
lastForm.show();
consumer.accept(true);
});
cancelBtn.addActionListener(a -> {
lastForm.showBack();
consumer.accept(false);
});
}
where BooleanConsumer is a trivial void accept(boolean b) interface.
There's another method for use from other threads
#Override public final boolean ask() {
assert !CN.isEdt();
final BooleanTransfer transfer = new BooleanTransfer();
CN.callSerially(() -> showAndThen(result -> transfer.set(result)));
return transfer.await();
}
where BooleanTransfer is a two-method class where the thread calling set passes a boolean to the thread calling await.

How to reset my GUI in Java with a JMenuItem?

I have a public JFrame with a CardLayout, this JFrame includes some JPanels for different stages of analysis, I want to return to the main panel and erase all data stored in the objects to start a new analysis using a JMenuItem, but I don't know which function could do that. Any suggestion?
I have tried with this code, a jMenuItem1ActionPerformed which just backs to that jpanel but doesn't reset the gui. "Seleccion" is the main jpanel, the main menu of the application
panelPrincipal.removeAll();
panelPrincipal.revalidate();
panelPrincipal.repaint();
panelPrincipal.add(seleccion);
panelPrincipal.revalidate();
panelPrincipal.repaint();
There is no "one-size-fits-all" solution, and there is no core Java "function" that you can call for this since it all depends on the structure of your program. In other words, you will have to create your own reset mechanism. Hopefully, your program structure is built around an Model-View-Control (MVC) type of pattern, and if so, then your JMenuItem's listener would notify the Control of the user's wish to reset, the Control would call reset() on the Model (a method which you would have to create of course) which would reset the Model to the initial state. The View which should have listeners attached to the Model will then change its display accordingly.
From your codes you probably tried every means you can think of to reset everything to initial state.
Personally, I do not think it is a good idea to remove all Components and add it back simply just because you want to reset it. You have to write your own codes to specifically tell Java what things need to change (reset).
For example if you have 3 text fields, you can do it in your action listener:
public void actionPerformed(ActionEvent e){
txtField1.setText("");
txtField2.setText("");
txtField3.setText("");
}
Doing this works, but it looks pretty much hard-coded and it is hard to maintain. Image if you have 999 textfields to deal with. You can always improve your program structure for example like:
input --> update database --> text fields read from database
Input updates database. The fields just read from the database. If you want to reset everything, just clear of the data in the database.
public void actionPerformed(ActionEvent e){
//Delete records from database
//Instead of updating all 999 fields.
}
This is just an example. It is up to you to decide how your program shall be structured.
repaint() basically just inform the paintManager to call the paintComponent() method, you probably won't see any difference in your UI appearance for calling that unless you have been doing things like overriding paintComponent() and making changes to the look and feel of the JComponents or using Graphics for drawing.

How to handle form data while switching between multiple frames

I have created a swing application in Netbeans environment. This application consists of four frames. Now my requirement is, after filling A frame when user clicks "Next" button then A's data should be buffered somewhere and then go to second form and so on. Finally when user lands in the last form, at this point i want the total data of all forms should be stored in database.
I am not sure how to achieve this. Can anyone suggest some useful resource or idea?
Consider having one non-GUI model class, that can be passed into the view (GUI) classes, either via a constructor parameter, or a setter parameter, i.e., public void setModel(Model model). Then all views can have the same Model instance passed into them.
When the first window has its data "submitted", it submits it into the one shared model. If you're using a Model-View-Control structure, then this is usually done by the Control, but if your program is simple, a Model-View is probably all you need. The other windows can be notified by the Model of the changed data, and then change their displays accordingly.
But as an aside, also please ask yourself, how many professional applications do you use that jump from window to window? Not many because it is a very distracting and often unpleasant experience for the user. Most use a single main application window that swaps gui views within this window and with an occasional dialog window popping up when information is needed in a modal fashion. Please have a look at The Use of Multiple JFrames, Good/Bad Practice?

Java Remote Client GUI

I've set up a Server which runs and accepts connections from my Remote Client, and now I'm working on my GUI.
Before anything else, my goal here is to create a nice looking client that will have a login screen (login/pw), and then a nice layout with my options/perhaps a chat box after the user has logged in.
I've searched a lot online and used this site to set up my server and get things working, but I've got a bit of a problem with the GUI/theory and hope someone here can guide me a bit.
At the moment, I've set up a class called ClientGUI which is called from my main class, and this produces a 420x240 size screen. After placing my login/password JTextField boxes here, is it "proper" to set up the other GUI's the way I've outlined below? I'm not sure if I should be putting them under one class or how I would advance from one GUI to the next. I'm thinking I should repaint and resize the screen as necessary, but I am not sure how to set it all up. A brief outline would be helpful (you don't need to give me exact code).
public class ClientGUI extends JFrame {
public ClientGUI() {
setSize(420,240);
setVisible(true);
setTitle("Title");
setResizable(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(null);
}
public loginGUI() {
//code for my login/pw boxes, images, listener for entering information
}
public afterlogginginGUI() {
}
paint() {
//not too sure about how this should be setup either. Should I do all my textfield
//and image work in paint()?
}
}
I have never made anything like this, so I have the feeling I'm not setting this up in an ideal way.
An alternative is to have a different java class extending JFrame for each 'screen' I want, but if I do it this way, would I do it like this?
In my main RemoteClient class:
main {
ClientGUI();
//display whatever
LoginGUI();
//listen for login info
if (loginIsValid) {
afterlogginginGUI();
}
}
I think you're thinking in to much of a linear fashion, where the code flows from A then to B then to C ... where in fact, Swing (and GUI's in general) are event driven...
C happens, so you do B, which triggers F so you do E ...
Start by creating a JPanel, onto this add your JTextField and JPasswordField, this will act as you basic login view. You could then add this to a JFrame or JDialog depending on your needs. You will need some way for the user to either "cancel" or "validate" their credentials.
Typically, I do this a separate view, as I never know where my poor "user details" pane might end up, but you could do this a single view (including the buttons within the "user details" pane), that will come down to your requirements.
You can use a CardLayout to switch from the "login" view to the "application" view. This has the benefit of maintaining only a single frame and prevents windows from been splashed all about the place...
I would, personally, separate the core functionality of the views to separate classes, this means you can simply create an instance when you need it and add it to whatever container you want.
I would recommend against extending from JFrame directly. This locks you into a single container (making it hard to re-use components or extend the program later) and you're not adding any new functionality to the class anyway...
Start by having a look at Creating a GUI With JFC/Swing.
You'll probably also be interested in How to Use CardLayout, How to Make Dialogs, How to Use Buttons, Check Boxes, and Radio Buttons and How to Write an Action Listeners
You'll also need to have a look at Laying Out Components Within a Container
Because you're likely waiting for a response from the server at some point, you will need to have a look at Concurrency in Swing and Worker Threads and SwingWorker wouldn't hurt

Is there something wrong with Swing's MVC implementation for JList?

Some time ago I asked this question. All solutions are workarounds.
Now this can't be. I feel that something is wrong here, but I can't tell if it is Swing's MVC model that is conceptually wrong, or if it is my thinking that is conceptually wrong.
Here is the problem again. I am using a JList to implement a list of thumbnails for the pages of a document. If the user selects another thumbnail from the list, that page is loaded. To do this I added a ListSelectionListener to the JList, which when the selection changes, it loads that page. But the user can also change the page using another control. Naturally, I want this to be reflected in the thumbnail list by having that page selected here. So I setSelectedIndex() to update the JList. Unfortunately this has the unwanted effect of raising a ListSelectionEvent which causes the listener to reload the page.
Now what is wrong here? I just changed the model from somewhere else, so naturally I want the view to update itself, but I don't want it to trigger events. Is Swing not implementing MVC right? Or am I missing a point here?
This is an issue that a lot of us Swing programmers have to face: multiple controls modifying the same data, with the update then reflected in each control. At some point, something has to have the ultimate veto on what updates will be applied to the model: whatever that something is needs to be able to handle multiple (potentially redundant or even contradictory) updates and decide what to do with them. This could happen in the model layer, but ideally it should be the controller that does this - this piece, after all, is most likely where the business logic resides.
The problem with Swing, in this regard, is that the controller piece of MVC is often split somewhat between the view component and the model so it can be difficult to have that logic centralised. To some extent the Action interface rectifies this for actionPerformed() events by putting the logic in one place and allowing it to be shared by different components, but this doesn't help for the other types of event, or when there are multiple different classes of event that need to be coordinated.
The answer, then, is to follow a pattern that's hinted at in Swing but not made explicit: only perform the requested update if the state will actually change, otherwise do nothing. An example of this is in the JList itself: if you attempt to set the selected index of a JList to the same index that's already selected nothing will happen. No events will be fired, no updates will occur: the update request is effectively ignored. This is a good thing. This means that you can, for example, have a listener on your JList that will respond to a newly selected item, and then in turn ask the same JList to reselect that same item and you won't get stuck in a pathologically recursive loop. If all the model-controllers in an application do this then there's no problem with multiple, repeated events firing off all over the place - each component will only update itself (and subsequently fire off events) if it needs to, and if it does update then it can fire off all the update events it wants, but only those components that haven't already got the message will do anything about it.
This is expected behaviour.
From Model-View-Controller [Wikipedia]:
In event-driven systems, the model
notifies observers (usually views)
when the information changes so that
they can react.
So, when you call setSelectedIndex on the JList, you are updating its model, which then notifies each ListSelectionListener. It wouldn't be MVC if you could "silently" update a model without letting anyone know.
Swing is not exactly MVC, but has it's roots in MVC ( the difference lays in the fact, the view and the controller in Swing are more closely related than in other MVC see Swing architecture for more details ).
But this may not seem to be the problem you're facing. It sounds like you have to validate in the event listeners, for the type of event and decide whether to ignore it or not: If the event was originated in the list do change it. If was triggered by some other control, do not.
What #dogbane said.
But to fix the problem you need to add some sort of a state check during the listener to see if the event is one you should ignore. The ListSelectionEvent has a getValueAdjusting() method, but that is mostly internal. What you need to do is simulate it yourself.
So for example when you update the list from an external selection you would have code like...
try {
setSelectionAdjusting(true);
/* ... your old update code ... */
} finally {
setSelectionAdjusting(false);
}
and in the update code for the ListSelectionListenerEvent
public void valueChanged(ListSelectionEvent e) {
if (!isSelectionAdjusting()) {
/* ... do what you did before ...*/
}
}
Scoping and access issues are left as an exercise for the reader. You would have to write the setSelectionAdjusting, and possibly set it on other objects as well.
I still feel like there is something conceptually wrong here.
I empathize, but it may help to consider that you don't have a simple JList observing a ListSelectionModel. Instead, you have a JList and some-other-control observing a hybrid selection model. In #Taisin's example, the hybrid is a CustomSelectionModel that extends DefaultListSelectionModel and allows silent changes. When compatible, it's also possible to share a model, as suggested in this question & answer and this SharedModelDemo from the tutorial.
For reference, this thread cites the article Java SE Application Design With MVC: Issues With Application Design, which addresses the issue in more detail.
I always do like this:
public class MyDialog extends JDialog {
private boolean silentGUIChange = false;
public void updateGUI {
try {
silenGUIChange = true;
// DO GUI-Updates here:
textField.setText("...");
checkBox.setSelected (...);
}
finally {
silentGUIChange = false;
}
}
private void addListeners () {
checkBox.addChangeListener (new ChangeListener () {
public void stateChanged (ChangeEvent e) {
if (silentGUIChange)
return;
// update MODEL
model.setValue(checkBox.isSelected());
}
});
}
}
Typically the way a listener works is it will "go off" every time the event that it is waiting for occurs. If I had to speculate it is a misunderstanding from your side of things.

Categories

Resources