Full disclaimer: I'm a CS student, and this question is related to a recently assigned Java program for Object-Oriented Programming. Although we've done some console stuff, this is the first time we've worked with a GUI and Swing or Awt. We were given some code that created a window with some text and a button that rotated through different colors for the text. We were then asked to modify the program to create radio buttons for the colors instead—this was also intended to give us practice researching an API. I've already handed in my assignment and received permission from my instructor to post my code here.
What's the best way to implement button actions in Java? After some fiddling around, I created the buttons like this:
class HelloComponent3 extends JComponent
implements MouseMotionListener, ActionListener
{
int messageX = 75, messageY= 175;
String theMessage;
String redString = "red", blueString = "blue", greenString = "green";
String magentaString = "magenta", blackString = "black", resetString = "reset";
JButton resetButton;
JRadioButton redButton, blueButton, greenButton, magentaButton, blackButton;
ButtonGroup colorButtons;
public HelloComponent3(String message) {
theMessage = message;
//intialize the reset button
resetButton = new JButton("Reset");
resetButton.setActionCommand(resetString);
resetButton.addActionListener(this);
//intialize our radio buttons with actions and labels
redButton = new JRadioButton("Red");
redButton.setActionCommand(redString);
...
And added action listeners...
redButton.addActionListener(this);
blueButton.addActionListener(this);
...
A stub was already created for the actionPerformed method to give us an idea on how to use it, but since there was only a single button in the template, it wasn't clear how to implement multiple buttons. I tried switching on a String, but quickly realized that, since a String isn't a primitive type, I couldn't use it for a switch statement. I could have improvised with an if-else chain, but this is what I came up with instead. It seems far from elegant, and there must be a better way. If there is, what is it? Is there a way to switch on a string? Or choose an action in a more scaleable fashion?
public void actionPerformed(ActionEvent e){
if (e.getActionCommand().equals(resetString)) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
return;
}
if ( e.getActionCommand().equals(redString) ) {
setForeground(Color.red);
repaint();
return;
}
if ( e.getActionCommand().equals(blueString) ) {
setForeground(Color.blue);
repaint();
return;
}
if ( e.getActionCommand().equals(greenString) ) {
setForeground(Color.green);
repaint();
return;
}
if ( e.getActionCommand().equals(magentaString) ) {
setForeground(Color.magenta);
repaint();
return;
}
if ( e.getActionCommand().equals(blackString) ) {
setForeground(Color.black);
repaint();
return;
}
}
Instead of writing this:
resetButton.addActionListener(this);
You could also write this:
resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
resetButtonActionPerformed(evt);
}
});
And instead of writing one big actionPerformed() for all actions, you can (and then have to) write this:
public void resetButtonActionPerformed(ActionEvent evt) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
}
I don't know if this is the most elegant solution, but at least you no longer have that big if construct.
Two alternate approaches:
Create a new class that implements the Action interface and has a Color field and an actionPerformed method that sets the color
Mantain a HashMap from command names to Color instances and look up the command name in the map
One decent enough approach is to declare an enum whose elements match your strings and switch on valueOf(str) (the linked example shows how to do this with a fair amount of safety).
The reason to avoid anonymous inner classes is probably because the class hasn't had that construct (yet), even though that might be the best solution.
As suggested already, you can use anonymous inner classes to implement the ActionListener interface. As an alternative, you don't have to use anonymous inner classes, but you can use a simple nested class instead:
resetButton = new JButton(new ResetAction());
redButton = new JButton(new ColorAction("Red", Color.red));
and then...
private class ResetAction extends AbstractAction {
public ResetAction() {
super("Reset");
}
public void actionPerformed(ActionEvent e) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
}
}
private class ResetAction extends AbstractAction {
private Color color;
public ColorAction(String title, Color color) {
super(title);
this.color = color;
}
public void actionPerformed(ActionEvent e) {
setForeground(color);
repaint();
}
}
For why this approach - or any approach involving inner classes - is better than implementing ActionListener in the outer class see "Design Patterns":
"Favor 'object composition' over 'class inheritance'." (Gang of Four 1995:20)
Choosing between anonymous inner classes and these named inner classes is a largely a matter of style, but I think this version is easier to understand, and clearer when there are lots of actions.
Ergh. Don't implement masses of unrelated interfaces in one mega class. Instead, use anoymous inner classes. They are a bit verbose, but are what you want. Use one for each event, then you wont need big if-else chain. I suggest keeping enough code within the inner class to decode the event and call methods that make sense to the target objects. Further, you can parameterise your inner classes. You will probably find you don't need to keep references to the actual widgets around.
In your example you seem to be using a JComponent as a JPanel. There's not much difference, but use JPanel for collecting a block of widgets. Further there is unlikely any need to subclass it, so don't.
So for instance:
addColorButton("Green" , Color.GREEN );
addColorButton("Red" , Color.RED );
addColorButton("Yellow", Color.YELLOW);
addColorButton("Blue" , Color.BLUE );
...
private void addColorButton(String label, Color color) {
JRadioButton button = new JRadioButton(label);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
target.setForeground(color);
target.repaint();
}
});
colorGroup.add(button);
panel.add(button);
}
Related
so I'm building a menu for a board game and would like players to be able to set their color with Choosers.
addChoosers(){
JPanel _choosers = new Jpanel();
_choosers.add(new JLabel("Green"));
_green = new Choice();
_green.add("Man");
_green.add("Machine");
_choosers.add(_blue);
_choosers.add(new JLabel("Blue"));
_blue = new Choice();
_blue.add("Man");
_blue.add("Machine");
_choosers.add(_blue);
}
I'd like to build one listener that knows from which chooser it was called and therefore the controller to set up the machine turns in the correct order.
public class PlayerListener implements ItemListener{
#Override
public void itemStateChanged(ItemEvent e) {
)
{//a bunch of else ifs here for each color
if (e.getItem()=="Man")
//do naught
else
//set the appropriate position in turn orientation
}
}
what i'm really asking for here are on which terms to build my if conditions. does e.getItem() return the String of the option, i.e. "Man" or "Machine"? and does e.getSource() return "Green"?
Thanks!
PS: I hope this is enough code to make my question understood.
I'm new here but I did some research before posting. My goal is to create a simple tower defense game using a couple of interesting ideas and moreover to train my development skills using javax.swing and java.awt. As far as I know, developers are mostly lazy guys and they do everything to make their life more simple.
There is map with a grid and for map loading my game uses a boolean matrix and a loading method to locate terrain on panels. I thought it will be quite simple solution. Because the matrix is 12 x 12, I would like to create it with some other application rather than entering a line of 144 numbers.
Here comes an idea to first create a map editor application and later do maps for levels in it. When I have such a tool I could make that map visually and then save its boolean matrix to a file, which later can be read by loading method and recreated in game. Next step is to make graphics and also panels that would react properly on user's actions. On the left there is a panel with buttons - after user clicks one of them, the field currentColor changes.
This field is used by method that implements actionListener and makes color change of the panel that is declared in its constructor. I wanted to change color of certain panel when its clicked. I use colors because its easier for now to make it working, later I want to replace color with a texture - obviously, I know I have to use a paintComponent method, but I assume that will work for it too, right? Also would be nice if the panel border changes color when I move my cursor over it and changes it back to normal when mouse is somewhere else.
The point here is that I'm having some trouble to make panels interactive. First problem is that panels are created in for loop and that makes it difficult to refer to a certain panel while mouse is over it. Another one comes with that I would like to change appearance of that panel after I click on it.
As far as I know, MouseListeners should do the work, but how to actually write it to have an effect on screen? I found some post about that, but for me it doesn't work. Here's the link: highlighting panels in java
My code:
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class Editor extends JFrame
{
private JButton towers = new JButton(new ImageIcon("pu.gif"));
private JButton road = new JButton(new ImageIcon("pu.gif"));
private JButton start = new JButton(new ImageIcon("pu.gif"));
private JButton finish = new JButton(new ImageIcon("pu.gif"));
private String mapTitle = "testmap";
private Color currentColor;
private int width = Toolkit.getDefaultToolkit().getScreenSize().width;
private int height = Toolkit.getDefaultToolkit().getScreenSize().height;
private String currentMapType = "Standard";
private static final int currentHeight = 12;
private static final int currentWidth = 12;
private JPanel[][] currentMapPanel;
private int[][] currentMapField;
//Toolbar - a panel with buttons
private JPanel panel = new JPanel(new GridLayout(10,3));
//Container for map - a panel with map
private Dimension containerSize = new Dimension(height, height);
static JPanel container = new JPanel(new GridLayout(currentHeight, currentWidth), true);
//Separator
private JSplitPane separator = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panel, container);
public Editor()
{
initComponents();
}
public void initComponents()
{
this.setTitle(mapTitle + ".map" + " - " + "Game Map Editor");
this.setSize(800, 600);
int frameWidth = this.getSize().width;
int frameHeight = this.getSize().height;
this.setLocation((width - frameWidth) / 2, (height - frameHeight) / 2);
this.setIconImage(Toolkit.getDefaultToolkit().getImage("pu.gif"));
towers.addActionListener(e -> {
currentColor = Color.CYAN;
System.out.println(currentColor);
});
road.addActionListener(e -> {
currentColor = Color.GRAY;
System.out.println(currentColor);
});
start.addActionListener(e -> {
currentColor = Color.LIGHT_GRAY;
System.out.println(currentColor);
});
finish.addActionListener(e -> {
currentColor = Color.BLACK;
System.out.println(currentColor);
});
new Map(currentMapType, currentWidth, currentHeight, false);
panel.add(towers);
panel.add(road);
panel.add(start);
panel.add(finish);
this.getContentPane().add(separator);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
/**
* Class that allows to load the graphic map and to view it in JFrame
*/
public class Map
{
public Map(String mapType, int rows, int columns, boolean load)
{
if (!load)
{
currentMapPanel = mapPanel(rows, columns);
currentMapField = new MapGenerator().mapFieldEmpty(rows, columns);
mapLoader(currentMapField, currentMapPanel);
}
else
{
currentMapPanel = mapPanel(rows, columns);
currentMapField = new MapGenerator().mapFieldGenerator(rows, columns);
mapLoader(currentMapField, currentMapPanel);
}
}
private JPanel[][] mapPanel(int rows, int columns)
{
JPanel[][] mapPanel = new JPanel[rows][columns];
for (int i = 0; i < rows - 1; i++)
{
for (int j = 0; j < columns - 1; j++)
{
mapPanel[i][j] = new JPanel(true);
mapPanel[i][j].setPreferredSize(new Dimension(height/12, height/12));
mapPanel[i][j].setBorder(new LineBorder(Color.BLACK));
mapPanel[i][j].addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
super.mouseEntered(e);
JPanel parent = (JPanel) e.getSource();
new colorListener(parent, Color.LIGHT_GRAY);
parent.revalidate();
}
#Override
public void mouseExited(MouseEvent e)
{
super.mouseExited(e);
JPanel parent = (JPanel) e.getSource();
new colorListener(parent, Color.GREEN);
parent.revalidate();
}
#Override
public void mouseClicked(MouseEvent e)
{
super.mouseClicked(e);
JPanel parent = (JPanel) e.getSource();
new colorListener(parent, currentColor);
parent.revalidate();
}
});
}
}
return mapPanel;
}
private void mapLoader(int[][] mapField, JPanel[][] mapPanel)
{
for (int i = 0; i < mapField.length - 1; i++)
{
for (int j = 0; j < mapField.length - 1; j++)
{
if (mapField[i][j] == 0)
{
mapPanel[i][j].setBackground(Color.GREEN);
container.add(mapPanel[i][j]);
}
else if (mapField[i][j] == 1)
{
mapPanel[i][j].setBackground(Color.GRAY);
container.add(mapPanel[i][j]);
}
else if (mapField[i][j] == 2)
{
mapPanel[i][j].setBackground(Color.LIGHT_GRAY);
container.add(mapPanel[i][j]);
}
else if (mapField[i][j] == 3)
{
mapPanel[i][j].setBackground(Color.BLACK);
container.add(mapPanel[i][j]);
}
else
{
System.out.println("An error occurred...");
}
}
}
}
private JPanel mapContainer(int rows, int columns)
{
container = new JPanel();
container.setLayout(createLayout(rows, columns));
container.setPreferredSize(containerSize);
container.setBounds(height/4, height/4, containerSize.width, containerSize.height);
return container;
}
private GridLayout createLayout(int rows, int columns){
GridLayout layout = new GridLayout(rows, columns);
return layout;
}
}
private class colorListener implements ActionListener
{
public colorListener(JPanel p, Color c)
{
this.panel = p;
this.color = c;
}
#Override
public void actionPerformed(ActionEvent e) {
panel.setBackground(color);
}
JPanel panel;
Color color;
}
public static void main(String[] args) {
new Editor().setVisible(true);
}
}
The question is broad and the answer complicated.
Essentially, you want to do some research into concepts such as "separation of responsibilities" and "decoupling code".
The idea is that you break down you functionality requirements so that your objects are doing a single, specialised job. You also "decouple" the code so that changing the implementation of one part won't adversely affect other parts of the program. This is commonly achieved through the use of interfaces.
You will also want to investigate the concept of "model-view-controller", where by the "data" or "state" is modelled in one or more classes, but is wholly independent of the UI. The UI is then free to "render" the model in what ever way it feels is appropriate.
In this way, the "view" (interacting with the controller) can change the state (or react to the change in state) of the model, making it easier to mange (no seriously, it does)
Code Review ...
This...
static JPanel container = new JPanel(new GridLayout(currentHeight, currentWidth), true);
is dangerous and a bad idea. It voids the concept of encapsulation and allows any one to create new instance of container at any time, without notification, which will disconnect it from what the program was previously using. In fact, you actually do this.
static is not your friend. Used correctly, it's useful, but used in this way, it's just a bad idea and should be avoid.
You should instead favour "dependency injection", where the "elements" that any one object relies on are passed to it.
I would avoid things like...
this.setSize(800, 600);
int frameWidth = this.getSize().width;
int frameHeight = this.getSize().height;
this.setLocation((width - frameWidth) / 2, (height - frameHeight) / 2);
Windows are complicated components, which also contain window decorations which wrap about the content. This means that the available space to the content is window size - window decorations. Instead. You should rely on the layout manager API to provide appropriate sizing hints and pack the frame.
On most modern OSs you have "other" system elements, which, again, reduces the amount of available space on the screen (docks, task bars, other funky stuff). Instead, you can use setLocationRelativeTo(null) to centre the window more reliably on the screen.
Instead of setIconImage, you should be using Window#setIconImages(List), which allows you to pass a number of images which can be used by the API to represent the application in different places that require different resolution images.
Not sure what ...
new Map(currentMapType, currentWidth, currentHeight, false);
but it's not really helping.
If you find yourself just creating an instance of class without actually maintaining a reference to it, then it's probably a good sign of a bad design.
Your Map class raises a bunch of questions which aren't easily answered. It kind of makes me worried that the Map class is modifying the state of the parent class and screams "dependency injection" instead.
This...
mapPanel[i][j].setPreferredSize(new Dimension(height / 12, height / 12));
is best avoided. You should prefer overriding getPreferredSize and it should simply return a "desired" size, which could then be used by things like GridLayout to layout the component more effectively.
This then leads into the "separation of responsibility". This section suggestions you should have a "tile" class, which would be self managed and responsible for a single element from the model.
There are a number of things wrong with your mouse event handling...
#Override
public void mouseEntered(MouseEvent e) {
super.mouseEntered(e);
JPanel parent = (JPanel) e.getSource();
new colorListener(parent, Color.LIGHT_GRAY);
parent.revalidate();
}
You shouldn't be calling super.mouseXxx(e) on of the jobs of those methods is to call the delegate MouseListeners, so, mess right there.
You can more easily use e.getComponent() to get a reference to the component which generated the event, but if panel was a self contained unit of work (ie Tile) and the MouseListener an anonymous or inner class, you'd be able to forego the cast altogether.
new colorListener(parent, Color.LIGHT_GRAY); scares me as it's setting up a bunch of strongly references objects which can't be easily dereferenced, nor am I clear on there intent.
parent.revalidate(); isn't doing what you seem to think it's doing.
revalidate generates a new layout pass, what you seem to want is repaint.
These...
container.setPreferredSize(containerSize);
container.setBounds(height / 4, height / 4, containerSize.width, containerSize.height);
are just bad ideas. Let the content of the container, along with the layout manager deal with.
So, the short answer is, you have a lot of research left to do, things like:
OO design patterns
OO good practices, including "separation of responsibilities", "code decoupling" and in a more general sense, "dependency injection"
Model-View-Controller, coding to interface instead of implementation
just to name a few
I am a novice coder, have a tiny bit of experience with C++ about 10 years ago and now learning java (it's been about 4-5 months). I have a little collaborative project going, and I've got some things to figure out.
Here's the code:
import java.awt.event.*;
import java.awt.*;
import javax.swing.JFrame;
import java.util.Scanner;
import java.util.Random;
public class Game extends JFrame {
GamePanel panel;
int[][] grid;
int size;
//...and some other variables
public Game(String title) {
super(title);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new GamePanel(grid,size);
this.add(panel);
Button button = new Button("WHAT");
button.setBounds(-100, -100, 70, 70);
this.add(button);
button.addKeyListener(new KeyHandler());
}
class KeyHandler extends KeyAdapter {
public void keyPressed(KeyEvent e) {
int keycode = e.getKeyCode();
switch(keycode) {
//arrow keys input and stuff
}
if(checkForWin()) //checkForWin() returns a boolean value for win/loss
//won the game, thus end the thread here
if(checkForLoss()) // similar stuff as checkForLoss()
//lost the game, thus end the thread here
//...update the panel
panel.repaint();
}
}
public static void main(String [] args) {
Game me = new Game("GAME");
me.setVisible(true);
}
}
So that's pretty much how the whole game thing looks like.
And I have questions:
I am using a button and put it at a negative position to make it invisible and using it as a mean of KeyListener. Is there any other way to add a key listener? ex) to the panel or something?
I want to change it so that each "level" is a thread and make it like
public static void main(String [] args) {
int level = 1;
do {
GameThread gt = new GameThread("Game");
// run() will have the game constructor inside
gt.setLevel(level);
gt.start();
gt.join(); //wait until the game "level" is finished
if(won)
level++;
if(lost)
level = 1;
} while(!checkIfDonePlaying())
}
Somewhat like this. I'm having trouble making the thread continue to run until the game level is actually finished. How do I do that?
I want to add a JLabel to show the score on the frame. But when I do that, the score doesn't update when I repaint() it. How do I do that?
Thanks in advance!
A few things:
Yes, there is a way to add a KeyListener to the panel, and that's by using key bindings. For example:
javax.swing.InputMap im = panel.getInputMap(panel.WHEN_IN_FOCUSED_WINDOW);
javax.swing.ActionMap am = panel.getActionMap();
im.put(javax.swing.KeyStroke.getKeyStroke("pressed UP"), "up");
am.put("up", new javax.swing.AbstractAction() {
#Override
public void actionPerformed(ActionEvent ev) {
// handle up arrow key action here
}
});
Swing is an event-driven environment, so do-while loops should not be used. Instead, use a Swing timer that periodically checks if your level has been completed.
Since you're not doing any custom painting on your JLabel, you shouldn't be using repaint. Instead, use its setText method.
Here's a solution for your first issue:
panel.setFocusable(true);
panel.addKeyListener(this);
Then you should just be able to do the usual key listener methods. Be sure to implement KeyListener!
Solutions for your other issues on their way. (Actually take #TNT 's suggestion and use keybindings, I'm more comfortable with listeners but for games I usually use lwjgl or slick2d)
2.
I feel that running your programs levels may be a bit inefficient (that may just be me) I would suggest having one thread in a run method and have the following:
Public void reset(int level)
{
//reset all variables based on local variable level...
}
Call it like this:
Reset(++currentLevel)); //use ++ before currentLevel so it adds to it and then resets
And you could easily use switch cases to do some special stuff if you want
Switch(level){
case 1: //...
}
And so forth. (I'm typing on mobile sorry for weird caps)
Not sure how well I will explain this; I'm quite new to programming...
So I'm trying to make a desktop application that draws musical notes of a given type on some sheet music when the user selects the button corresponding to that type of note. Currently, if the user selects the "Whole Note" button, the user can then start clicking on the screen and the note will be drawn where the click occurred. It will also make a "ding" sound and write some info about that note to a text file.
That's all well and good, but unfortunately when the user selects a new button, say the "Quarter Note" button, for each mouse click there will be two notes drawn (one whole, one quarter), two dings, and two packets of info written to the file. I have no idea how to make this work! Currently, I'm trying to use threads, such that each button creates a new thread and the thread currently in use is interrupted when a new button is pressed, but that doesn't resolve the issue.
Initially, an empty linked list of threads ("NoteThreads") is constructed. Also, a private class known as SheetMusicPane (given the variable name "smp") is constructed in order to draw the sheet music. The buttons are added in the main constructor (public CompositionStudio), whereas the method containing the mouse listener (see what follows) is contained in the SheetMusicPane private class. Not sure whether that is part of the problem.
I have a button action listener:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!threads.isEmpty()) {
NoteThread oldThread = threads.remove();
oldThread.interrupt();
}
NoteThread newThread = new NoteThread(e.getActionCommand());
threads.add(newThread);
newThread.run();
}
});
that produces a thread:
private class NoteThread extends Thread {
private String note;
public NoteThread(String note) {
this.note = note;
}
public void run() {
smp.getShape(smp.getGraphics(), note);
}
}
that when, on running, calls this method with graphics and a mouse listener:
public void getShape(final Graphics g, final String note) {
this.addMouseListener(new MouseListener() {
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
Point p = MouseInfo.getPointerInfo().getLocation();
addShape(g, p.x, p.y, note);
int pitch = 12;
piano.playNote(pitch);
advance(1.0, piano);
try { addToFile(pitch, note);}
catch(FileNotFoundException fnfe) {}
catch(IOException ioe) {}
}
});
}
The above method is responsible for drawing the note ("addShape()"), making the ding sound, and writing to the file.
Thanks in advance for any help you can give!
what you're trying to do does not require multithreading. This is the approach that I'd take:
set up a set of toggle buttons or radio buttons to select the note to paint. this way, only one note will be selected at a time. add action listeners to those that store in an adequately scoped variable what note is selected, or infer that every time a note should be drawn. this way, you don't even add action listeners to the buttons. in any case, don't spawn new threads.
in your mouse listener, find out what note to draw, and do that - only one note.
if you can, stay away from multithreading, especially as a beginner. also, I think you confuse adding and running listeners here. each call to getShape() adds a new listener, meaning they accumulate over time, which might be the cause of your problems.
PS: welcome to stackoverflow! your question contained the important information and I could infer that you tried solving the problem yourself. It's pleasant to answer such questions!
One solution would be to simply fetch all the listeners (which should be 1) and remove them before adding the new listener:
public void getShape(final Graphics g, final String note) {
MouseListener[] listeners = this.getMouseListeners();
for (MouseListener ml : listeners) {
this.removeMouseListener(ml);
}
this.addMouseListener(new MouseListener()...);
}
An alternative, since you have a finite number of buttons, would be to create a finite set of listeners, eg:
private MouseListener wholeNote = new MouseListener()...;
private MouseListener quarterNote = new MouseListener()...;
Create a reference to the "current" listener (private MouseListener current;), and have a means of deciding which listener to use whenever getShape is called (a series of if conditions on the note String would work, although I would prefer some refactoring to use an enum personally). Then you could do something along the lines of:
private MouseListener wholeNote = new MouseListener()...;
private MouseListener quarterNote = new MouseListener()...;
private MouseListener current;
...
public void getShape(final Graphics g, final String note) {
if (current != null) {
this.removeMouseListener(current);
}
if (...) { // note is Whole Note
current = wholeNote;
} else if (...) { // note is Quarter Note
current = quarterNote;
} // etc.
this.addMouseListener(current);
}
Another alternative would be to change your listener so that you only ever need the one, but clicking a button changes a variable which the listener has access to. For example:
// In the listener
public void mouseClicked(MouseEvent e) {
Point p = MouseInfo.getPointerInfo().getLocation();
addShape(g, p.x, p.y, currentNote);
int pitch = 12;
piano.playNote(pitch);
advance(1.0, piano);
try { addToFile(pitch, currentNote);}
catch(FileNotFoundException fnfe) {}
catch(IOException ioe) {}
}
// In the same class
protected String currentNote;
...
public void getShape(final Graphics g, final String note) {
currentNote = note;
}
Is it possible to pass a String to an ActionListener? I'm creating a number guessing game, and I need to pass the difficulty selected to the the ActionListener, because it's passed from the first GUI opened. How would I do this as it's passed directly to the Game() constructor?
Have your ActionListener access a variable that contains the result of the guess.
I would do something like:
final JTextArea textArea = new JTextArea();
JButton button =new JButton("press");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
String text = textArea.getText();
if (text.equals("something")) {
doSomething();
} else {
doSomethingElse();
}
}
});
Where does the string come from? You can pass that component to the ActionListener when constructed. For example, your string comes from a JLabel object. Then
class GuessNumberActionListener implements ActionListener {
private JLabel difficulty;
public GuessNumberActionListener(JLabel difficulty) {
this.difficulty = difficulty;
}
// implement other methods
}
Then inside your action listener, you can access/update what you want.
ActionListener is an interface. try extending the functionality of the interface using inheritance. this will do the trick
Sure you can. There are lots of ways but one of them is to pass it in to the the ActionListener's constructor:
public class MyClass implements ActionListener {
private int difficultyLevel;
public MyClass(int difficultyLevel) {
this.difficultyLevel = difficultyLevel;
}
public void actionPerformed(ActionEvent e) {
...//code that reacts to action and does something based on difficultyLevel
}
}
UPDATE:
Looks like the design patterns police are out in full force today. You may want to quickly rewrite your app in MVC before you get shot in the foot :)
http://java.sun.com/products/jfc/tsc/articles/architecture/