I wanted the information to be shown when a radio button is clicked. The program look like this program. When I click one of the radio button and press ENTER, I should be getting the result on the right textfields but It seems like there was nothing displayed on the right textfields when i choose a radio button. There was no error in my code though. Other than that, I was able to choose both of the buttons, how can I set it as only one of the radio button will be true?
public class Q2 extends JFrame implements ItemListener{...}
public Q2()
{
Font ft1 = new Font ("Dialog", Font.BOLD+Font.ITALIC,30);
Font ft2 = new Font ("Arial", Font.BOLD,15);
lb2 = new JLabel("Calculate fee by:");
lb2.setFont(ft1);
rb1 = new JRadioButton("Kilometer (RM0.95 per/km)");
lb1 = new JLabel("Welcome to MyTeksiSapu");
lb1.setForeground(Color.WHITE);
lb1.setFont(ft1);
lb3 = new JLabel("Distance to destination(km): ");
tf1 = new JTextField(10);
rb2 = new JRadioButton("Time (RM0.35 per/min)");
lb4 = new JLabel("Distance to destination(mins): ");
tf2 = new JTextField(10);
lb5 = new JLabel("Calculated by: ");
tf3 = new JTextField(10);
tf3.setFont(ft2);
tf3.setForeground(Color.red);
tf3.setOpaque(false);
lb6 = new JLabel("Total: RM ");
tf4 = new JTextField(10);
tf4.setFont(ft2);
tf4.setForeground(Color.red);
tf4.setOpaque(false);
JPanel p1 = new JPanel();
p1.add(lb1);
p1.setBackground(Color.GREEN);
JPanel subp2 = new JPanel();
subp2.setLayout(new GridLayout(2,3));
subp2.add(rb1);subp2.add(lb3);subp2.add(tf1);
subp2.add(rb2);subp2.add(lb4);subp2.add(tf2);
JPanel p2 = new JPanel();
p2.setLayout(new GridLayout(2,1));
p2.add(lb2);
p2.setBackground(Color.lightGray);
p2.add(subp2);
JPanel subp3 = new JPanel();
subp3.setLayout(new GridLayout(2,2));
subp3.add(lb5);subp3.add(tf3);
subp3.add(lb6);subp3.add(tf4);
JPanel p3 = new JPanel();
p3.setLayout(new GridLayout(2,2));
ImageIcon icon = new ImageIcon(getClass().getResource("taxicar.jpg"));
JLabel taxicar = new JLabel(icon);
p3.add(taxicar);
p3.setBackground(Color.white);
p3.add(subp3);
add(p1,BorderLayout.NORTH);
add(p2,BorderLayout.CENTER);
add(p3,BorderLayout.EAST);
}
public void itemStateChanged(ItemEvent e)
{
DecimalFormat df = new DecimalFormat("0.00");
double km = Double.parseDouble(tf1.getText());
double mins = Double.parseDouble(tf2.getText());
double total = 0;
if(rb1.isSelected())
{
tf3.setText("Meter");
total = 0.95*km;
tf3.setText(df.format(total));
}
if(rb2.isSelected())
{
tf3.setText("Time");
total = 0.95*mins;
tf3.setText(df.format(total));
}
}
Beyond the simple functionality of an application (works vs. doesn't work), there are general guidelines and principles that should be observed to ensure the best possible performance is achieved when creating an application. I have a few observations on what's wrong with this application beyond the OP's question
Responsibility of components
The first mistake I found with the OP's implementation was with the assigned responsibilities of the JFrame. In a swing application, the responsibility of the JFrame should be limited to provide main container for the application itself. With these, there are some derived responsibilities that I won't get into. However, I would say that the JFrame is not responsible to act as a middle man between Events and Components. Therefore, in this context, JFrame implements ItemListener is clearly a mistake. When you look at what the itemStateChanged method does, it obvious that it doesn't belong there. Moreover, if the JFrame ever needs to execute some logic based on changes in its own state, the implementation of this method will be forced to add a new responsibility to it; violating SOLID's Single Responsibility Principle. On the other hand, if the implementation of the listener is limited to the radio buttons, it his very unlikely this will change in the future. For now, let's assume that the radio buttons will need to implement ItemListener (although I will argue against this shortly).
To fix this code, you could simply add an anonymous listener to the
buttons directly. Search the web for details on how to do this.
You could also opt to create your own class that extends
JRadioButton where your specialized class can address all of the
dependencies your components will need for your application or
project. For homework (like this one appears to be) this is not
necessary.
A better option to anonymous listeners, is to create a private class
that implements the desired listener.
public class MyClass {
...
private class MyListener implements .... {
// implemented methods here
}
}
Then, your component can simply create an instance of MyListener and use it in as many components that might need it OR create as many instances of MyListener and assigned to specific components, but this looks like anonymous listeners would be better.
Expected behavior of radio buttons
Radio buttons are buttons (remember this point). However, they are a specialized form of buttons. When people see radio buttons, they expect that the selection of a radio button automatically deselects the previously selected buttons. This application doesn't do that. In this example, both radio buttons can be in the selected state at the same time. This behavior is expected from JCheckBox, but not from JRadioButton. In order for radio buttons to activate the behavior I just described, they must be added to a ButtonGroup.
ButtonGroup group = new ButtonGroup();
group.add(rb1);
group.add(rb2);
Once this is done, controlling the deselection of previously selected button in the group is handled automatically. Besides the point of this being how most people expect radio buttons to behave like, this application requires this behavior. Why, because text fields set the text they are supposed to display based on the EXCLUSIVE selection of one of the radio buttons. For this application, one text field is responsible to display whether the taxi fare was calculated based on distance OR time; not both.
Lastly, typically one of the radio buttons will be selected by default. HOWEVER, in this context, I think it is OK for the application to launch with both deselected. Doing this will eliminate the confusion of seeing one selected and no text in the fields that get populated based on radio button's selection.
Event Listeners
Event listeners are at the heart of Event-Driver Programming, which is basically how modern applications are built. Java Swing framework has a very robust set of listeners for almost all situations. In my many years working Swing applications, I don't recall a time where I designed my own listener.
Remember I stated earlier that "radio buttons are buttons" and that I also wrote about "responsibility of components"? I stated that to frame why I oppose the use of ItemListener for triggering the logic of what happens when a radio button is selected. From the ItemSelectable interface (which AbstractButton extends), this interface was created or designed to address the following responsibility:
The interface for objects which contain a set of items for which zero or more can be selected
Based on that, you can see this sounds very close to what ButtonGroup does. If you examine this class, you can see why AbstractButton extends this interface. ButtonGroup needs it in order to "create a multiple-exclusion scope for a set of buttons." This is how radio buttons assigned to a button group get automatically deselected when another one is selected. Could the same listener be used to fire off the logic to update the text fields based on the radio button that is selected? Yes, it can be done this way. But based on the listener's documentation, this wasn't the reason why it was created.
My suggestion is to implement ActionListener instead. This class is not only a general-purpose listener, it is the one recommended by the designers of the Swing framework to handle such events. In this case, my solution is very simple:
private class RadioSelectionListener implements ActionListener {
#Override
public void actionPerformed (ActionEvent e) {
JRadioButton rb = (JRadioButton)e.getSource(); // As the creator, I know this listener is added to radio buttons only. So, the assumption is safe
DecimalFormat df = new DecimalFormat("0.00");
double km = Double.parseDouble(tf1.getText());
double mins = Double.parseDouble(tf2.getText());
double total = 0;
if (rb.getActionCommand().equals("km")) {
tf3.setText("Meter");
total = 0.95 * km;
} else {
tf3.setText("Time");
total = 0.95 * mins;
}
tf4.setText(df.format(total)); // This was set on tf3 in the OP's code which is wrong
}
}
In order for this solution to work, the same instance of ActionListener should be added and an "action command" should be assigned to each button. Although this is not necessary, it makes it much easier to know which of the button is associated with the event's source (as you can see in my code snippet above). Therefore, somewhere in the application, the following lines must be added:
rb1.setActionCommand("km");
rb2.setActionCommand("time");
ActionListener listener = new RadioSelectionListener();
rb1.addActionListener(listener);
rb2.addActionListener(listener);
"Never" extend JFrame
I typed the word never inside quotes because this is not a rule set in stone. This is more of a personal preference, but one I find to be shared by a whole lot of people. Instead, you should create an "app" class that contains a JFrame along with the rest of the components. This "app" should be the class containing the main method to launch your app. The recommended approach to do this is the following:
public class MyApp {
public static void main (String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run () {
createAndShowGUI();
}
private void createAndShowGUI () {
// construct and add your frame and other items here.
}
});
}
}
Summary
Remember Single Responsibility Principle and assign responsibility where it belongs. Here, the components that need to be registered to receive notification when they are clicked are the buttons themselves, not the frame.
Encapsulate behavior and attributes in classes to improve usability. It also improves readability. In this case, the frame should not be a "god class" with hundreds lines of code.
Lastly, implement behaviors that closely match the expectations or mental models of the users. For radio buttons, only one in a group is expected to be selected at any given time. In fact, they got their name from old radios with preset buttons that, when pressed, they would change the station selection. Imagine how bad it would've been if radios allowed multiple stations to be selected at the same time?
There are a few more (minor) issues I would like to address, but this gone long enough... I included screenshots of the application working after my changes.
Related
Is it possible to create a custom version of a swing component, say jbutton, that restricts access to certain methods of jbutton?
I want to have a jbutton (or various swing elements) where I define certain elements that can't be changed and certain that can. Using jbutton as an example, I'd like to allow a user of the class to add a text, or image to the button, set it enabled or disabled or set the size etc, but I want to define the look of the button and not have that modified.
If I extend jbutton using my custom class, I can only override each method I don't want modified with an unsupported exception. If I extend J component, I will have to rewrite basically all of jbutton. Is there a better way?
I'm asking because we are making a set of custom J components that clients can use to build hmi screens. We want to limit elements based on our look and feel and hmi standards.
You can extend a JPanel or wrap your button (or even both of them).
First variant
public class MyButton extends JPanel {
private JButton button = new JButton();
public MyButton() {
init();
}
// probably some another constructors.
private void init() {
setLayout(new GridLayout()); // button take the entire place of the panel
add(button);
}
public void setText(String text) {
button.setText(text);
}
}
Second variant:
public class MyButton {
private JButton button = new JButton();
public MyButton() {
}
// probably some another constructors.
public JComponent getComponent() {
return button;
}
public void setText(String text) {
button.setText(text);
}
}
Both of these variants allow you to hide the restricted functionality without to reimplement JButton or throw exceptions in overriden methods. You can also combine these two variants or each of these variants with throwing exception for restricted methods (it could be interesting, because some experienced developers can access JButton object, by casting to JButton the result of the method getComponent()).
I have a quick question. I don't get it...
I've got a JFrame where I add a JComboBox:
JComboBox<String> Team_ComboBox = new JComboBox<>();
Team_ComboBox_Handler ComboBox_Listener = new Team_ComboBox_Handler();
Team_ComboBox.addActionListener(ComboBox_Listener);
Team_ComboBox.addItem("Test 1");
Team_ComboBox.addItem("Test 2");
On this Frame I have a button which opens another JFrame.
Play = new JButton();
Play.setText("Play");
Play.setPreferredSize(dimension);
Play.addActionListener(menuhandler);
private class main_menuhandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==Play){
teams Team = new teams();
Team.teams();
disposeMainMenue();
}
if(e.getSource()==Close) {
System.exit(DO_NOTHING_ON_CLOSE);
}
}
}
Anyway, I would like to transfer the Selected value of the Combobox to a method of the other class. I know how I can get the itemvalue of the combobox in the method itself (with getselecteditem) But how can I do that in the ActionPerformed Method as I can't access the combobox in the ActionPerformed method.... I created another ActionListener (comboBox_Listener) but I haven't put any code into it...
Any idea? Thanks a lot in advance
Several issues appear to me:
Your main question:
But how can I do that in the ActionPerformed Method as I can't access the combobox in the ActionPerformed method
Your likely best solution is to change your code and variable declaration placement so that you can access the JComboBox fromt he actionPerformed method. If you're declaring the combobox from within a method or constructor, change this so that it is a proper instance field of the class.
Other problems:
You should not be creating multiple JFrames. If you need a dependent window, then one should be a JDialog. If not, then consider swapping views with a CardLayout.
Learn and follow Java naming conventnions so others can better understand your code. Class names begin with capital letters and methods and variable names don't for instance.
I am not sure why you're doing this: System.exit(DO_NOTHING_ON_CLOSE);. Why pass that constant into the exit method?
Use a constructor for your action listener class:
private class main_menuhandler implements ActionListener {
private JComboBox<String> Team_ComboBox;
public main_menuhandler(JComboBox<String> Team_ComboBox){
this.Team_ComboBox = Team_ComboBox;
}
}
Now you can create the class main_menuhandlervia the constructor and add the combobox to it.
In your Overriden action you have access to it.
Try playing around with this as your code snippet isn't broad enough to actually provide proper code. But this should answer your question
New to the forum and to Java. I am trying to have my JList respond when double-clicked, which I have accomplished. The JList is being populated by a SQL query which is ran when a button in the GUI is pressed. Based on the SQL query, the JList is populated, this is also working.
The issue comes about if I try to update the JList by clicking the button to query SQL again. When I click that, the change initially shows up in the JList, however when I click on that option in the JList it immediately switches back to what it was initially. When I double-click on what appears to be the incorrect name, the value that I have printing in the console reports correctly. So it has the value correct in the console but the rendering in the JList is not correct.
I appreciate any responses, I have combed the forums without any luck. I am new to Java so I'm sure there is quite a bit that isn't perfect with my code. Code is below please let me know if you need more. Thank you.
public JPanel results(StringBuilder message)
{
StringBuilder[] options = {message};
showOption = new JList(options);
showOption.setLocation(300, 50);
showOption.setSize(140,100);
showOption.setVisibleRowCount(10);
textPanel.add(showOption);
showOption.revalidate();
showOption.repaint();
MouseListener mouseListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
//JList showOption = (JList) mouseEvent.getSource();
if (e.getClickCount() == 2) {
int index = showOption.locationToIndex(e.getPoint());
Object o = showOption.getModel().getElementAt(index);
System.out.println("Double-clicked on: " + o.toString());
}
}
};
showOption.addMouseListener(mouseListener);
return totalGUI;
}
public static void main ( String args[] )
{
//JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("[=] JTextField of Dreams [=]");
GUI_TextField demo = new GUI_TextField();
frame.setContentPane(demo.createContentPane());
//frame.setContentPane(demo.results(message));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(510, 400);
frame.setVisible(true);
}
Three things jump out at me immediately.
You're creating a new JList each time
You're manually setting the size and position of the JList
You're not removing the previous JList
For example...
public JPanel results(StringBuilder message)
{
StringBuilder[] options = {message};
// Create new JList
showOption = new JList(options);
// This is ill advised
showOption.setLocation(300, 50);
showOption.setSize(140,100);
showOption.setVisibleRowCount(10);
// What about the last JList?
textPanel.add(showOption);
This raises a number of possibilities, the likely one is that you are covering over the previous list, which is being brought to the front when textPanel is validated and painted.
Swing follows (loosly) the MVC paradigm (and for more details)
So instead of re-creating the view each time, you should simply re-create the model, for example...
public JPanel results(StringBuilder message)
{
DefaultListModel model = new DefaultListModel();
model.addElement(message);
showOption.setModel(model);
If showOption isn't created initially before this method is called, you should consider putting in a if statement to detect when showOption is null and initialise it appropriately.
You should also avoid using setLocation and setSize. Swing has being designed to operate with the use of layout managers, these make it possible to define workflow and general layout that can be used across multiple platforms.
Take a look at How to use lists and Laying Out Components Within a Container
I'm writing a Java Swing application using the Metal look-and-feel. Every time there is a JButton in my application the user uses the Tab key to move the focus to the button and then hits the Enter key. Nothing happens! If he hits the Space key the button events are fired. How do I assign the Enter key to trigger the same events as the Space key? Thank you for your help.
I found the following:
http://tips4java.wordpress.com/2008/10/25/enter-key-and-button/
Where Rob Camick writes that when using JDK5 and later you simply add...
UIManager.put("Button.defaultButtonFollowsFocus", Boolean.TRUE);
...to the application to solve the problem. This did the trick for me! And I can't imagine anything simpler. However, when using older versions of Java you will have to do something like Richard and Peter describe in their answers to this question.
Here is complete example. Richard was close, but you also need to map pressed ENTER to action, not just released. To make it work for ALL buttons, I have put this mapping to default input map for buttons. Add imports, and it should be runnable.
public class Main implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main());
}
#Override
public void run() {
setupEnterActionForAllButtons();
JFrame frame = new JFrame("Button test");
frame.getContentPane().add(createButton(), BorderLayout.NORTH);
frame.getContentPane().add(createButton(), BorderLayout.SOUTH);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private void setupEnterActionForAllButtons() {
InputMap im = (InputMap) UIManager.getDefaults().get("Button.focusInputMap");
Object pressedAction = im.get(KeyStroke.getKeyStroke("pressed SPACE"));
Object releasedAction = im.get(KeyStroke.getKeyStroke("released SPACE"));
im.put(KeyStroke.getKeyStroke("pressed ENTER"), pressedAction);
im.put(KeyStroke.getKeyStroke("released ENTER"), releasedAction);
}
private JButton createButton() {
JButton b = new JButton("press enter");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Pressed");
}
});
return b;
}
}
Actually, this is a look and feel issue. It is (and should be) up to the look and feel as to which key triggers the focused button.
The "default button" work-around works since the L&F you're using uses enter for the default button.
Peter's workaround explicitly changes the L&F default "focus action" key - which is somewhat more convincing if you ask me.
I would add that I don't think many users would want to tab to the button then hit enter (most won't even notice the focus indicator) - they want the default action to be the "right" one and work wherever they press enter. This can only be done with input maps as Richard suggests.
I would certainly suggest getting a very clear picture of what your users actually want and expect (preferably with reference to other apps they use) before changing anything globally.
You do it by assigning an input / action map for the Enter key. Something like the following:
// save the command mapping for space
Object spaceMap = button.getInputMap.get(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true));
// add a mapping from enter to the same command.
button.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true),spaceMap);
You can also set the "default button" to the button most recently focussed.
I did this on an application, and all methods of doing this are a nightmare to maintain and debug. The fact is, this is clearly not what the designers of Swing intended to happen.
Extension to above answers to do same with radio , checkboxes.
Called this before creating components
void setupEnterAction(String componentName){
String keyName = componentName + ".focusInputMap";
InputMap im = (InputMap) UIManager.getDefaults().get(keyName);
Object pressedAction = im.get(KeyStroke.getKeyStroke("pressed SPACE"));
Object releasedAction = im.get(KeyStroke.getKeyStroke("released SPACE"));
im.put(KeyStroke.getKeyStroke("pressed ENTER"), pressedAction);
im.put(KeyStroke.getKeyStroke("released ENTER"), releasedAction);
}
public void setEnterEvent(){
setupEnterAction("Button");
setupEnterAction("RadioButton");
setupEnterAction("CheckBox");
}
I have a JFrame that contains a "display" JPanel with JTextField and a "control" JPanel with buttons that should access the contents of the display JPanel. I think my problem is related on how to use the observer pattern, which in principle I understand. You need to place listeners and update messages, but I don't have a clue where to put these, how to get access from one panel to the other and maybe if necessary to introduce a "datamodel" class. For example, I want to access the contents of the JTextField from the control panel and I use an anonymous action listener as follows:
JButton openfile = new JButton("Convert file");
openfile.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openButtonPressed();
}
});
You need to reduce the coupling between these objects.
You can have a master object, that owns all the text fields and the button ( the panels are irrelevant )
Then a separete actionlistener within that master object ( I call it mediator see mediator pattern )
That action listener performs a method on the mediator which in turn take the values from the textfields and create perhaps a transfer object.
This way you reduce the coupling between the panels, textfields etc. and let the control in one place ( the mediator ) that is, you don't let them know each other.
You can take a look at the code in this question:
https://stackoverflow.com/questions/324554/#324559
It shows these concepts in running code.
BTW the observer pattern is already implemented in the JTextField, JButton, ActionListener etc. You just need to add the hooks.
I hope this helps.
EDIT Joined two answers into one.
This is the code.
class App { // this is the mediator
// GUI components.
private JFrame frame;
private JTextField name;
private JTextField count;
private JTextField date;
// Result is displayed here.
private JTextArea textArea;
// Fired by this button.
private JButton go;
private ActionListener actionListener;
public App(){
actionListener = new ActionListener(){
public void actionPerformed( ActionEvent e ){
okButtonPressed();
}
};
}
private void okButtonPressed(){
// template is an object irrelevant to this code.
template.setData( getData() );
textArea.setText( template.getTransformedData() );
}
public void initialize(){
frame = new JFrame("Code challenge v0.1");
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
name = new JTextField();
count = new JTextField();
date = new JTextField();
textArea = new JTextArea();
go = new JButton("Go");
go.addActionListener( actionListener ); // prepare the button.
layoutComponents(); // a lot of panels are created here. Irrelevant.
}
}
Complete and running code can be retrieved here:
It is important to favor composition over inheritance when possible.
It does make the code cleaner if you create the models in one layer and add a layer or two above to create the components and layout. Certainly do not extend the likes of JFrame and JPanel.
Do not feel the need to make the composition hierarchy in the model layer exactly match the display. Then it's just a matter of taking the text from the Document and performing the relevant operation.
Okay, perhpas not that simple. Swing models are a little bit messy. In particular ButtonModel is brain damaged, and the controller area of code might not be entirely pure.
We have so called builders, which will build the parent panel out of the children. In this builder you will have access to all the subcomponents you need to listen to and can thus can implement any logic there.
Finally the builder will then return the parent panel with the complete logic.
Once you've got the parent panel it's really a mess getting to the child components and have them do anything.
thanks. I added a datamodel layer which handles somehow the communication between the panels.
I also found this link on Listeners on JTextField usefull:
link text