I thought I had a decent grasp on the this keyword. Something has me confused a little bit.
I have a method that adds an ActionListener to my button. This is what the method looks like
public void checkButtonState(){
button1.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
}
});
}
I also have a constructor method in my class.. which looks like this
public CanvasA(){
try{
CanvasABackground = ImageIO.read(new File("C:\\Users\\user\\workspace\\Interface\\src\\01120156745.jpg"));
}catch(IOException ex){
}
setSize(450,490);
setLayout(null);
JLabel picLabel = new JLabel(new ImageIcon(CanvasABackground));
add(picLabel);
createEnterButton();
createCloseButton();
checkButtonState();
checkButtonState2();
}
When using the this keyword in the constructor, I get many different methods that popup. For example typing this. will generate many methods just called add and many many others. However, typing this. inside the
#Override
public void actionPerformed(ActionEvent arg0) {
}
method, genereates completely different methods.. basically the this keyword is referring to something else. In the first case, it's talking about my CanvasA class (which extends JPanel btw). Im curious what this is referencng when typed inside the actionPerformed method.
Thanks for your help.
Where
new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
//here this referes to ActionListener
}
});
is an new Annaymous inner class ,inside that this referes to the current instance of ActionListener
this here is referring to your action listener instance.
Related
I realize this is a repeat question, but my circumstances are a little bit different. I need to have a MouseListener in another class that can altar the background color of the object that calls it. Please help me.
public class LeftListPanel extends JPanel {
public LeftListPanel() {
setBackground(Settings.BACKGROUND_COLOR);
setLayout(null);
addPersonalStatsTab();
}
private void addPersonalStatsTab() {
JPanel personalStatsPanel = new JPanel();
personalStatsPanel.addMouseListener(new CustomMouseListener());
JLabel personalStatsText = new JLabel("Text");
personalStatsPanel.add(personalStatsText);
add(personalStatsPanel);
}
Then I have an inner-nested class for the MouseListener because this is the only place this MouseListener will be called.
class CustomMouseListener implements MouseListener {
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
setBackground(Settings.BACKGROUND_COLOR.brighter());
}
#Override
public void mouseExited(MouseEvent e) {
setBackground(Settings.BACKGROUND_COLOR);
}
}
The setBackground(COLOR) lines are those who don't work... this.setBack and super.setBack ARE NOT working in this case.. I'M DESPERATE FOR HELP!
The reason you don't see the background changes is that when you call setBackground, you are de-referring (implicitly) the this object, i.e. the instance of LeftListPanel. So, you are actually changing its background, but you don't see it because inside the LeftListPanel instance there is another JPanel (instantiated at the addPersonalStatsTab method) which occupies the whole visible space (or even it is not visible at all, because of that weird null layout; I don't know exactly).
Fist of all, I recommend to you not to set null as a layout. Chose a proper layout, or let it be defaulted - do not call setLayout(null).
Then, set personalStatsPanel as a private member of LeftListPanel. And when calling to setBackground, use it as the scope reference:
LeftListPanel.this.personalStatsPanel.setBackground(...);
This works, I instead just created a private method where I pass in the panel I want to apply it too.
private void CustomMouseListener(JPanel panel) {
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
panel.setBackground(Settings.BACKGROUND_COLOR.brighter());
}
#Override
public void mouseExited(MouseEvent e) {
panel.setBackground(Settings.BACKGROUND_COLOR);
}
});
}
Thank you all for your time and suggestions :)
You could...
Pass a reference of the component you want changed to the CustomMouseListener
class CustomMouseListener implements MouseListener {
private JPanel panel;
public CustomMouseListener(JPanel panel) {
this.panel = panel;
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
panel.setBackground(Settings.BACKGROUND_COLOR.brighter());
}
#Override
public void mouseExited(MouseEvent e) {
panel.setBackground(Settings.BACKGROUND_COLOR);
}
}
This is okay if you want to use the listener on a limited number of components, but if you want to use the same listener on a number of components...
You could...
Use the source property of the MouseEvent to get which component triggered the event
#Override
public void mouseEntered(MouseEvent e) {
if (!(e.getSource() instanceof JPanel)) {
return;
}
JPanel panel = (JPanel)e.getSource();
panel.setBackground(Settings.BACKGROUND_COLOR.brighter());
}
or, a better solution would be to do something more like...
#Override
public void mouseEntered(MouseEvent e) {
e.getComponent().setBackground(Settings.BACKGROUND_COLOR.brighter());
}
since the information is already provided to you (just not, this returns an instance of Component, so if you need to access the Swing specific properties, you'd still need to cast it).
Why is this approach better?
CustomMouseListener listener = new CustomMouseListener();
panel1.addMouseListener(listener);
panel2.addMouseListener(listener);
panel3.addMouseListener(listener);
panel4.addMouseListener(listener);
panel5.addMouseListener(listener);
panel6.addMouseListener(listener);
panel7.addMouseListener(listener);
because it's agnostic, meaning you can create a single instance of the listener and re-use on multiple components
I've been trying to implement this action listener as stated above but keep receiving two errors:
-Cannot instantiate the type ActionListener
-void is an invalid type for the variable incrementAction
I've been looking up similar examples but they all seem to point to the same method of implementing it.
This is where I've got to.
increment.addActionListener(new ActionListener());{
public void incrementAction(ActionEvent e){
this.incrementCount();
this.setTextField();
}
}
The signature of the ActionListener method is:
public void actionPerformed(ActionEvent e)
JButton increment = new JButton();
increment.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("ActionEvent received! ");
}
});
This is my first attempt of using a keylistener in Java, so don't be harsh on me. I am trying to use TimerListener and KeyListener within the same separate private class to supplement the main activities in my upper class.
public class GameMemory {
private int[][] grid = new int[24][12];
Figure figure = new Figure();
GameMemory() {
figure.figureReinitialize();
Timer timer = new Timer(1000, new TimerListener());
timer.start();
}
And the private class
private class TimerListener implements ActionListener, KeyListener {
#Override
public void actionPerformed(ActionEvent e) {
figure.moveDown();
System.out.println(Arrays.deepToString(grid)); // To debug.
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT)
GameMemory.this.figure.rotateRight();
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
}
I did an extensive research and watched several online videos and read oracle documentation on the topic, but still can't get much sense out of it. In the current state the code compiles, and timer runs as expected, but I think I am missing a statement somewhere...
I tried addKeyListener in GameMemory constructor, but it said it cannot find symbol.
Any ideas?
If the timer is being called but the key isn't then most likely you haven't added the listener as a KeyListener. That very much looks like what is happening in your code as you put a timer on it but then do nothing else with it.
In general it would be better practice to split them anyway though. Each Java class should try and focus on doing one thing and doing it well.
is it possible in java to have a class where it has EventHandlers for with different functions? for example button1 will log you in, while button2 will log you out, is this possible? Here's the code I made it seems to be not working.
package event.handlers;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TheHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent logInEvent) {
System.out.println("Button Login");
}
public void actionPerformed(ActionEvent cancelEvent) {
System.out.println("Cancel Login");
}
}
You either need two implementations of ActionListener, one for each button or the actionPerformed needs to determine the button by the event argument and take the appropriate action. Your code will not compile because the signatures for both methods are the same.
No. You can not have a class implement two methods with the same function signature. How would the compiler know which one to call for different events? The name you give to the arguments has no meaning to the compiler.
As an alternative, you can create multiple anonymous action listeners that simply forward the call to a method that does have a unique name if you want everything to be in the same class.
public class TheHandler {
public TheHandler() {
JButton login, cancel;
//initialize code here
login.addActionListener( new ActionListener() {
#Override
public void actionPerformed(ActionEvent logInEvent) {
loginPerformed(logInEvent);
}
});
cancel.addActionListener( new ActionListener() {
#Override
public void actionPerformed(ActionEvent cancelEvent) {
cancelPerformed(cancelEvent);
}
});
}
public void loginPerformed(ActionEvent logInEvent) {
System.out.println("Button Login");
}
public void cancelPerformed(ActionEvent cancelEvent) {
System.out.println("Cancel Login");
}
}
You may use getSource() or getActionCommand() method of ActionEvent.
#Override
public void actionPerformed(ActionEvent logInEvent) {
Object src=logInEvent.getSource();
String cmd=logInEvent.getActionCommand(); //It will return caption of button
if(src==btn1)
{
//
}
//Or
if(cmd.equals("Button1")) { ... }
}
You can not have multiple actionPerformed method in one class. Simple way is to do operation based on source of action like:
(in actionPerformed method)
if(e.getSource() == loginButtton) { // based on button variable if they are in same class and accessible in actionPerformed method
loginMethod()
} else if(e.getSource == logoutButton) {
logoutMethod()
}
or
if(e.getActionCommand().equals("loginButtton")) { // based on caption/text on button
loginMethod()
} else if(e.getActionCommand().equals("logoutButtton")) {
logoutMethod()
}
or you can have different anonymous class for different buttons like
loginButton.addActionListner(new ActionListerner(){
public void actionPerformed(ActionEvent loginEvent) {
loginMethod();
}
});
logoutButton.addActionListner(new ActionListerner(){
public void actionPerformed(ActionEvent cancelEvent) {
logoutMethod();
}
});
The problem there is that your two method signatures are identical. When Java tries to figure out which method to call, it can't tell the difference between the two.
I can think of two ways to do what you want:
Presumably, you are registering the listeners on the buttons like cancelButton.addActionListener(...). So you can either provide each button with its own anonymous inner class:
loginButton.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent logInEvent) {
System.out.println("Button Login");
}
}
cancelButton.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent cancelEvent) {
System.out.println("Cancel Login");
}
}
or you can define a single actionPerformed method that checks the source of the call:
public class TheHandler implements ActionListener {
JButton loginButton;
JButton cancelButton;
public TheHandler()
{
...
// Now, technically, this is bad form because you're leaking 'this'.
// But as long as this will only be called after this constructor finishes
// initializing, it's safe.
loginButton.addActionListener(this);
cancelButton.addActionListener(this);
...
}
...
#Override
public void actionPerformed(ActionEvent evt) {
if(evt.getSource() == loginButton)
System.out.println("Button Login");
else if(evt.getSource() == cancelButton)
System.out.println("Cancel Login");
}
}
Using anonymous inner classes can sometimes be clearer, because you see the code right next to the addListener call, but it also adds a lot of boilerplate, and if you're working on a very large progect that can take a while to load, reducing the number of classes can sometimes make it load a little faster (each anonymous inner class is another thing for the JVM to load).
How would I go about writing a constructor for an inner class which is implementing an interface? I know I could make a whole new class, but I figure there's got to be a way to do something along the line of this:
JButton b = new JButton(new AbstractAction() {
public AbstractAction() {
super("This is a button");
}
public void actionPerformed(ActionEvent e) {
System.out.println("button clicked");
}
});
When I enter this it doesn't recognize the AbstractAction method as a constructor (compiler asks for return type). Does anyone have an idea?
Just insert the parameters after the name of the extended class:
JButton b = new JButton(new AbstractAction("This is a button") {
public void actionPerformed(ActionEvent e) {
System.out.println("button clicked");
}
});
Also, you can use an initialization block:
JButton b = new JButton(new AbstractAction() {
{
// Write initialization code here (as if it is inside a no-arg constructor)
setLabel("This is a button")
}
public void actionPerformed(ActionEvent e) {
System.out.println("button clicked");
}
});
If you really need a contructor for whatever reason, then you can use an initialization block:
JButton b = new JButton(new AbstractAction() {
{
// Do whatever initialisation you want here.
}
public void actionPerformed(ActionEvent e) {
System.out.println("button clicked");
}
});
But you can't call a super-class constructor from there. As Itay said though, you can just pass the argument you want into the call to new.
Personally though, I would create a new inner class for this:
private class MyAction extends AbstractAction {
public MyAction() {
super("This is a button.");
}
public void actionPerformed(ActionEvent e) {
System.out.println("button clicked");
}
}
then:
JButton b = new JButton(new MyAction());
The resulting class is not of type AbstractAction but of some (unnamed, anonymous) type that extends/implements AbstractAction. Therefore a constructor for this anonymous class would need to have this 'unknown' name, but not AbstractAction.
It's like normal extension/implementation: if you define a class House extends Building and construct a House you name the constructor House and not Building (or AbstractAction just to com back to the original question).
The reason the compiler is complaining is because you are trying to declare a constructor inside your anonymous class, which is not allowed for anonymous classes to have. Like others have said, you can either solve this by using an instance initializer or by converting it to a non-anonymous class, so you can write a constructor for it.