I’m relatively new to using the javax.swing and java.awt so bear with me if I express my problem awkwardly.
Let’s say I have a custom made class CustomClass that extends and creates a JPanel p. In the class I add a JButton b to p. Later in another program file I create an instance of my CustomClass called cp and want to be able to catch for example a click event from b using the “actionPerformed” method. My question is how do I “reach” (like the written path to) the JButton b from instance cp? (Assuming that all relevant class files are already associated)
Use getters and setters if i understood correctly. I,e your customPanel will have a public getButton() method which would return the JButton instance:
class CustomPanel extends JPanel {
JButton button=new JButton("Some button");
public JButton getMyButton() {
return button;
}
}
class Test {
CustomPanel cp=new CustomPanel();
void someMethod() {
JButton b= cp.getMyButton();
}
}
UPDATE
as per comment:
what if I have like 10 or 20 different components in my JPanel, is
there some way to reach them without having to make a lot of methods
Simply call getComponentCount on JPanel instance and than iterate using a for loop and getComponentAt(int i) this will allow you to get access to all components on JPanel:
CustomPanel cp=...;//this class extends jpanel
for(int i=0;i<cp.getComponentCount();i++) {
Component c=cp.getComponentAt(i);
if( c instanceof JButton) {
//do something
}
}
UPDATE 2
What if I have two or more objects that should be of the same class
but otherwise treated as separate objects, how can I tell them apart
using the loop that you've provided me
look at setName(String name) and getName of JButton this will allow you to assign the instance a unique name which can be gotten by getName(). Alternatively use setActionCommand(String name) and getActionCommand() to differentiate the buttons from another I prefer the latter.
Or you could even use their texts, via getText()
Related
I'm trying to make a button in a class which removes something from a table. I have it working when it's not in a separate class. However, when I try and move the code to a new class and create an object of that class in my main method, the button doesn't work. I'm pretty new to using Swing, so any help will be much appreciated.
public abstract class RemoveButton extends frame implements ActionListener {
public void actionPerformed(ActionEvent arg0) {
DefaultTableModel model = (DefaultTableModel) table.getModel();
if (table.getSelectedRow() == -1) {
if (table.getRowCount() == 0) {
Mess.setText("Table is empty");
} else {
Mess.setText("You need to select");
}
} else {
model.removeRow(table.getSelectedRow());
}
}
}
}
This class is the button which is supposed to remove a guest. I extended the frame, because that is where most of my variables are. From here, I am not so sure as of how to call this to my main method and get it working. All the imports have been added.
I'm going to guess here, which we're forced to do since the question as written presently is incomplete, but I suspect that you're misusing inheritance. I'm guessing that your main GUI is held by the frame class (which should be re-named to comply with Java standards so that it's first letter is upper case), and are doing this so that this class can call methods and access fields of the frame class. If so, then you're using inheritance for the wrong purpose since the "frame" instance held by RemoveButton is unique and completely different from the frame instance that is displayed.
Instead, you should pass into this class a reference to the actual displayed frame object, and then call methods on this object as needed.
e.g.,
// why abstract? I've removed that
// no longer extends frame
public class RemoveButton implements ActionListener {
// I've renamed your frame class to "MyFrame"
private MyFrame myFrame;
// constructor that allows you to pass in MyFrame reference
public RemoveButton(MyFrame myFrame) {
this.myFrame = myFrame; // assign to field
}
public void actionPerformed(ActionEvent evt) {
JTable table = myFrame.getTable(); // give MyFrame this method
DefaultTableModel model = (DefaultTableModel) table.getModel();
// .... etc
}
}
Cleaner would be to use a MVC design pattern, but that would require some more up-front work.
I am trying to create a JFrame in Java, with a JPanel inside of it, which will hold a component.
I know how to add components using
panelname.add(component);
But I am making a class based off a JTextField and want to add the entire CLASS as a component into the JPanel, but when I do, Eclipse tells me:
The method add(Component) in the type Container is not applicable for the arguments (BetterText)
(BetterText been the name of the class)
So all it basically is, is a class with a JTextField setup with methods and such, but I want to add that class as a component to the JPanel. I looked at the JTextField.java class and cant see anything interesting there, it looks like an ordinary class like any other, but you are able to add an instance of that class to a JPanel, whereas with mine, you cant.
Any help will be appreciated, thankyou.
Also, if you know the solution, please post an example class.
Edit: Added code.
public BetterText(String defaultText) {
super();
//Sets up the textFields colours and the defaultText to display in it.
setProperties();
hasDefault = true;
this.defaultText = defaultText;
textField.addActionListener(this);
}
Another edit:
It also extends JTextField already.
public class BetterText extends JTextField implements ActionListener {
Make sure your JFrame, JPanel and JTextField extend the correct classes (if they are custom classes).
Some pseudo code:
public class BetterText() extends JTextField{
public BetterText(){
super();
}
}
And then to create the GUI:
JFrame frame = new JFrame();
JPanel panel = new JPanel();
BetterText textField = new BetterText();
frame.add(panel);
panel.add(textField);
panel.pack();
panel.setVisible(true);
Verify that you are importing javax.swing.JComponent, then make your BetterText class inherit from JTextField.
This is how my code looked in the beginning: https://gist.github.com/anonymous/8270001
Now I removed the ActionListener to a separate class: https://gist.github.com/anonymous/8257038
The program should give me a little UI, but it just keeps running without any UI popup or errors.
Someone told me this:
In your GUI class constructor, you are creating a new nupuVajutus object, but since nupuVajutus extends the GUI class, when you create a nupuVajutus, you are also inherently calling the GUI class constructor by default, thus creating an infinite loop
If this is really the problem, then I have to say I am not that good and could use some help getting this program working with the classes separated.
You have indeed already been given the answer, although what you have is not an infinite loop, but infinite recursion, which will eventually cause a StackOverflowError.
Here's what happens:
new GUI() calls new nupuVajutus(). This creates a new nupuVajutus object by calling its constructor. Because nupuVajutus extends GUI, this means a nupuVajutus object is a GUI object with additional functionality. Therefore, because it is a GUI object, a GUI constructor needs to be called. The nupuVajutus constructor does not explicitly call a super constructor, so it implicitly calls the GUI() (no argument) constructor before executing. In this new call to the GUI() constructor, another new nupuVajutus() call is encountered, and so on, ad infinitum...
It seems to me you need to do some more research around Object Oriented Programming, in particular the topics of sub-classing, inheritance, object instances, and encapsulation. There are plenty of resources available to help you.
After you extracted your ActionListener into a separate file, you should not have changed it to extend GUI. That extends the class (which is like a blueprint) not an instance (which is like a something built using that blueprint) - remember: you can create multiple instances of a class.
Previously, the "nupuVajutus" ActionListener was an inner class, so it had access to all of the enclosing class' fields and methods. Now that it is no longer an inner class, it needs to be passed a reference to the GUI instance so that it can access its methods. Something like this:
public class NupuVajutus implements ActionListener {
private final GUI gui;
public NupuVajutus(GUI gui) {
this.gui = gui;
}
public void actionPerformed(ActionEvent e) {
// The GUI instance can now be accessed through the gui field, for example:
gui.something();
// ...
}
}
And in the GUI() constructor:
NupuVajutus nV = new NupuVajutus(this);
To be honest, though, there is nothing wrong with keeping your ActionListener as an inner class. If you're never going to use that class outside of the GUI class, then it is probably preferable for it to remain as an inner class.
What you are doing it extending the GUI class. This Does Not make then share the Same Fields Say you have a field field in your GUI class
public class GUI {
String field = "Hello";
}
Just because your Listener class extends GUI doesn't mean they will share the exact same field object. I think that's what you think is supposed to occur
public class Listener extends GUI implements ActionListener {
public void actionPerformed(ActionEvent e) {
field = "World";
}
}
The above does nothing the field in GUI. If you were to do this, you would need to access in a static way like line GUI.field = "World";. The above is also what causes in an infinite loop, as you need to instantiate the Listener in the GUI class. This is not really good practice or design.
One option would to use some sort of MVC pattern.
Another option would be to pass the values you need, to a constructor in your Listener class, and instantiate it in your GUI class with those values.
Run this example to see what I'm talking about. I have a MyListener class that I pass a Jlabel to, the same JLabel in the GUI class
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class FieldTest {
private JLabel label = new JLabel(" ");
private JButton button = new JButton("Set Text");
public FieldTest() {
MyListener listener = new MyListener(label);
button.addActionListener(listener);
JFrame frame = new JFrame();
frame.add(label, BorderLayout.CENTER);
frame.add(button, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new FieldTest();
}
});
}
}
class MyListener implements ActionListener {
JLabel label;
public MyListener(JLabel label) {
this.label = label;
}
#Override
public void actionPerformed(ActionEvent arg0) {
label.setText("Hello, FieldTest!");
}
}
I've just been trying to learn and messing around with code. And I've come across something I did not expect to happen. I have a JLabel in a MainApp class, I create an ActionListener (HelloListener) which is passed the JLabel. When the button is pressed, the actionPerformed method should update the JLabel to "Hello again!". And it does, but why it does it confuses me.
However, I thought I would have to return the new JLabel? When I pass the HelloListener the JLabel, isn't that JLabel the property of the HelloListener class after it is passed? So when it updates it will only update the one in HelloListener, and I would then have to return it?
Why when I update the JLabel in the HelloListener does it also update in the MainApp class?
Here's the code:
public class MainApp extends JFrame {
public static void main(String[] args) {
new MainApp();
}
public MainApp() {
setLayout(new GridLayout (2,1));
setSize(200,200);
JLabel jl = new JLabel("Hello!");
add(jl);
JButton jb = new JButton("Click me!");
jb.addActionListener(new HelloListener(jl));
add(jb);
setVisible(true);
}
}
and
public class HelloListener implements ActionListener {
JLabel jl;
public HelloListener(JLabel jl) {
this.jl = jl;
}
#Override
public void actionPerformed(ActionEvent arg0) {
jl.setText("Hello again!");
}
}
When I pass the HelloListener the JLabel, isn't that JLabel the
property of the HelloListener class after it is passed? So when it
updates it will only update the one in HelloListener, and I would then
have to return it?
This is where the breakdown in your understanding is. When you "pass the JLabel", you are passing a reference to the JLabel (you are actually passing the reference by value, which you should look up as soon as you understand your current issue, because it is very important with Java). The underlying object instance still exists everywhere it did before. So the JLabel jl in the HelloListener instance is just a reference to the same actual JLabel instance that is presented in the gui.
This is fundamental to how Java (and many programming languages) works. If you do
Dog d = new Dog();
and then pass d to a method
walk(d);
and walk looks like
public void walk(Dog dog) {
dog.setLastWalkTime(now());
dog.attachLeash();
}
dog in the method, and d in the calling scope, both point to the same Dog instance. So as sson as you set the lastWalkTime, the underlying instance is modified. If right after walk you looked at d, you would see the lastWalkTime value you set in the method.
When a function takes an object as an argument, it is being passed a reference to the object. In your code, both MainApp and HelloListener have a reference to the same JLabel instance.
When actionPerformed() is called, the listener modifies a property of the instance. Since the JFrame has access to that same instance, it detects the change and updates the GUI.
Why would you expect to return the new JLabel? (And then what? Remove the old one, add new one, have your window to do the layout again?) The JLabel instance you're passing into the listener is used as a reference to your JLabel, and that's the beauty of it - you're modifying your existing instance.
I want to add event handling to buttons- and I noticed that there are two ways to do this.
Implement the ActionListener interface and then append event listeners to the buttons.
Example:
countButton.addActionListener(this);
And the in the ActionPerformed method will run to display the result.
Do not implement the ActionListener interface and instead do this:
countButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
//Execute when button is pressed
System.out.println("You clicked the button");
}
});
How does the second method work exactly?????!!!
Thanks!
It is not necessary to define a second class for the first approach. You just need to add the
public void actionPerformed(ActionEvent e) method inside your class and do whatever you want there, after you make your class implement ActionListener. You could use a second class if you wanted to, but it is not necessary. The disadvantage is that you need to specify the source of the event with long if statements in order to take the appropriate action if you have more than one JButtons i.e.
The second approach is adding an anonymous inner ActionListener to every component. It is a more Object Oriented approach, since you have a clearer separation of the functionality of your widgets. It is of advantage to define an additional method called inside each actionPerformed, in order to be able to use this freely and refer to the containing class:
countButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
doCountButtonAction(e);
// "this" here would refer to the ActionListener (not that useful)
}
});
and implement the method:
private void doCountButtonAction(ActionEvent e) {
// do whatever you need to here
// using "this" here refers to the containing class (i.e. JFrame, JPanel or whatever)
}
Your question is not exactly on listeners as it is on how interfaces work and how you can instantiate a class in Java. Here's some finer points:
Basicaly, what the JButton class offers you is a way to declare a class who's one particular method will be called when an event is triggered on the button, like when it is clicked. If you look at the Javadocs for JButton and ActionListener, you will now how they work:
http://download.oracle.com/javase/1.4.2/docs/api/javax/swing/AbstractButton.html#addActionListener(java.awt.event.ActionListener)
http://download.oracle.com/javase/1.4.2/docs/api/java/awt/event/ActionListener.html
What you can do here in the most old fashioned way possible is to create a class that will be triggered when someone clicks your button:
public class MyButtonActionListener implements ActionListener {
actionPerformed(ActionEvent e) {
System.out.println("Aww, you clicked!");
}
}
Now, once that's done, you can make an insance of it and add it as a listener to your button:
JButton button = new JButton("My button");
MyButtonActionListener myActionListener = new MyButtonActionListener ();
button.addActionListener(myActionListener);
On the other hand, in Java you can instantiate a class anonimousy, which means that instead of having a handler to it's instance (like myActionListener in the above code), you just instantiate it on the fly in the place you need it, and you'll have no handler to use it later. That's what's happening in your code: an ActionListener implementation is delcared on the fly as the parameter for the addActionListener method, that on the fly declaration also includes the statement that your anonymous instance is not just any class, but one that implements ActionListener, and such your anonymous declaration needs to give an implementation of the actionPerformed method.
A third option is to have a class that implements ActionListener (and the actionPerformed method), and inside that class, if you create a JButton and you want to pass it as listener an instance of the ecompasing class, you'll use "this" to reffer to that, as in :
public class MyButtonActionListener implements ActionListener {
private JButton button = new JButton();
public void init() {
button.addActionListener(this);
}
public actionPerformed(ActionEvent e) {
System.out.println("u clicked!");
}
}
There's alot of finer points to this discussion (as in how do you reffer to the "this" on a n anonymous class delcared inside an other class, and how do you reffer to the "this" of the encompassing class instance). I recommend that you read a book on the Sun Certified Java Programmer certification, it has a chapter dedicated to this
That just creates an anonymous class that implements the actionPerformed method. This way you don't have to define a new class for every place that you want to handle an event.
It works the same way as the first technique i.e. The method expects an object which implement ActionListener interface. However, both techniques has its own merits and demerits. The first technique allows utilizing single object on the expense of cluttered if/else code because same method will be handling events for multiple buttons. The second technique allows cleaner separation of logic for each button on the expense of creating multiple anonymous objects (for each button).
Either way, to add an ActionListener you need a class that implements the ActionListener interface (which defines a single method, actionPerformed). addActionListener(this) implies that this implements that interface, and any action performed will call this.actionPerformed.
In the second case, what you're doing is creating a new, anonymous, class that implements the ActionListener interface. When countButton is clicked, the actionPerformed method called is the one in the anonymous class.