I am trying to make an MVC Java Swing program that makes use of SwingPropertyChangeSupport to notify the view whenever the model gets updated. The problem I am having is that the notifications do not seem to be happening.
I have prepared an SSCCE below. In the SSCCE, there is a Swing GUI that has a button and a text field. When you click the button, a counter in the model gets incremented, and the view is supposed to get notified so that it can update itself. However, it appears the notifications do not get sent/received (I am not sure which -- it could be both) even though I have checked to make sure that oldValue and newValue are different from each other. I would appreciate any assistance in understanding where I've gone wrong. Thanks!
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.SwingPropertyChangeSupport;
public class Main extends JFrame {
public Main() {
PropertyChangeView theGui = new PropertyChangeView();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(true);
add(theGui);
pack();
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Main();
}
});
}
}
class PropertyChangeView extends JPanel {
private JButton button;
private JTextField textfield;
private GridBagConstraints gbc;
private PropertyChangeController controller;
public PropertyChangeView() {
super(new GridBagLayout());
controller = new PropertyChangeController();
button = new JButton("Click me to increment the count");
textfield = new JTextField(10);
button.addActionListener(new ButtonListener());
addPropertyChangeListener(new MyPropertyChangeListener());
gbc = new GridBagConstraints();
gbc.gridheight = 1;
gbc.gridwidth = 1;
gbc.anchor = GridBagConstraints.LINE_START;
gbc.fill = GridBagConstraints.BOTH;
gbc.gridx = 0;
gbc.gridy = 0;
add(button, gbc);
gbc.gridx = 1;
gbc.gridy = 0;
add(textfield, gbc);
}
private class MyPropertyChangeListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println("Event received " + evt);
if (evt.getPropertyName().equals(PropertyChangeModel.CHANGED)) {
textfield.setText(evt.getNewValue().toString());
}
}
}
private class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
controller.setCounter(controller.getCounter() + 1);
System.out.println("counter now = " + controller.getCounter());
}
}
}
class PropertyChangeController {
private PropertyChangeModel model;
public PropertyChangeController() {
model = new PropertyChangeModel();
}
public int getCounter() {
return model.getCounter();
}
public void setCounter(int i) {
model.setCounter(i);
}
}
class PropertyChangeModel {
public static final String CHANGED = "property change model updated";
private int counter;
private SwingPropertyChangeSupport pcs;
public PropertyChangeModel() {
counter = 0;
pcs = new SwingPropertyChangeSupport(this);
}
public int getCounter() {
return counter;
}
public void setCounter(int i) {
int oldValue = counter;
int newValue = i;
counter = newValue;
pcs.firePropertyChange(CHANGED, oldValue, newValue);
System.out.println("setCounter finished with oldValue=" + oldValue + ", newValue=" + newValue);
}
}
I've not run your program, but I see one thing out of order here:
public void setCounter(int i) {
int oldValue = counter;
int newValue = i;
pcs.firePropertyChange(CHANGED, oldValue, newValue);
counter = newValue;
System.out.println("setCounter finished with oldValue=" + oldValue + ", newValue=" + newValue);
}
which should be:
public void setCounter(int i) {
int oldValue = counter;
int newValue = i;
counter = newValue;
pcs.firePropertyChange(CHANGED, oldValue, newValue);
System.out.println("setCounter finished with oldValue=" + oldValue + ", newValue=" + newValue);
}
You should fire notification only after updating the model's value.
Your main problem though is that you add no PropertyChangeListener to the model.
e.g.
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.SwingPropertyChangeSupport;
public class Main extends JFrame {
public Main() {
PropertyChangeView theGui = new PropertyChangeView();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(true);
add(theGui);
pack();
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Main();
}
});
}
}
class PropertyChangeView extends JPanel {
private JButton button;
private JTextField textfield;
private GridBagConstraints gbc;
private PropertyChangeController controller;
public PropertyChangeView() {
super(new GridBagLayout());
PropertyChangeModel model = new PropertyChangeModel();
controller = new PropertyChangeController(model);
button = new JButton("Click me to increment the count");
textfield = new JTextField(10);
button.addActionListener(new ButtonListener());
model.addPropertyChangeListener(new MyPropertyChangeListener());
gbc = new GridBagConstraints();
gbc.gridheight = 1;
gbc.gridwidth = 1;
gbc.anchor = GridBagConstraints.LINE_START;
gbc.fill = GridBagConstraints.BOTH;
gbc.gridx = 0;
gbc.gridy = 0;
add(button, gbc);
gbc.gridx = 1;
gbc.gridy = 0;
add(textfield, gbc);
}
private class MyPropertyChangeListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println("Event received " + evt);
if (evt.getPropertyName().equals(PropertyChangeModel.CHANGED)) {
textfield.setText(evt.getNewValue().toString());
}
}
}
private class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
controller.setCounter(controller.getCounter() + 1);
System.out.println("counter now = " + controller.getCounter());
}
}
}
class PropertyChangeController {
private PropertyChangeModel model;
// public PropertyChangeController() {
// model = new PropertyChangeModel();
// }
public PropertyChangeController(PropertyChangeModel model) {
this.model = model;
}
public int getCounter() {
return model.getCounter();
}
public void setCounter(int i) {
model.setCounter(i);
}
}
class PropertyChangeModel {
public static final String CHANGED = "property change model updated";
private int counter;
private SwingPropertyChangeSupport pcs;
public PropertyChangeModel() {
counter = 0;
pcs = new SwingPropertyChangeSupport(this);
}
public void addPropertyChangeListener(
PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
public int getCounter() {
return counter;
}
public void setCounter(int i) {
int oldValue = counter;
int newValue = i;
counter = newValue;
pcs.firePropertyChange(CHANGED, oldValue, newValue);
System.out.println("setCounter finished with oldValue=" + oldValue
+ ", newValue=" + newValue);
}
}
Related
I'm trying to build a simple rolling dice game for my assignment. There will be 2 players. In my code I try to use method getSource() so that when a player roll JButton is enabled, the other player's button will be disabled but when I run the game both player's buttons are still enabled. Here are my code:
Die.java
import java.util.*;
public class Die
{
private final int MAX = 6;
private int die1;
Random rand = new Random();
//Constructor
public Die()
{
die1 = 1;
}// end Constructor
public int Roll()
{
die1 = rand.nextInt(MAX)+1;
return die1;
}
}
DisplaySixNumbersPanel.java
import java.awt.*;
import javax.swing.*;
public class DisplaySixNumbersPanel
{
public static void main(String[ ] args)
{
JFrame w1 = new JFrame("Six Numbers Game");
w1.setLayout(new GridLayout(1,2));
SixNumbersPanel2 player1 =new SixNumbersPanel2();
SixNumbersPanel2 player2 =new SixNumbersPanel2();
w1.add(player1);
w1.add(player2);
w1.setSize(540, 350);
w1.setVisible(true);
w1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
} //end main
} //end class
SixNumbersPanel2.java
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
public class SixNumbersPanel2 extends JPanel implements ActionListener
{
private JTextField text1;
private boolean flag = false;
private boolean checkflag[] = {false,false,false,false,false,false,false};
private JLabel label;
private JLabel label1;
public JButton roll;
private JButton restartButton;
private JTextArea display;
private JTextField[] fields = new JTextField[7];
private Die dice = new Die();
private int key;
private int count;
private int count1;
private Player player1 = new Player();
private Player player2 = new Player();
private int check;
private int[] list = new int [7];
//private SixNumbersPanel2 player2panel;
public SixNumbersPanel2()
{
label= new JLabel ("A number between 1 to 6 wil appear");
label1 = new JLabel("once in each of the following textfields");
roll = new JButton("Roll dice");
restartButton = new JButton("Restart Game");
text1 = new JTextField(3);
display = new JTextArea(10,15);
add(label);
add(label1);
for(int i=1;i<7;i++)
{
fields[i] = new JTextField(3);
add(fields[i]);
}
add(roll);
add(text1);
add(display);
display.setEditable(false);
restartButton.setVisible(false);
add(restartButton);
restartButton.addActionListener(this);
roll.addActionListener(this);
}
public void restart()
{
display.setText("");
text1.setText("");
for(int i=1; i<7; i++)
{
fields[i].setText("");
}
count=0;
count1=0;
Arrays.fill(checkflag,false);
Arrays.fill(list,0);
flag = false;
restartButton.setVisible(false);
rollenable();
}
public void actionPerformed(ActionEvent event)
{
if(event.getSource() == roll)
{
player2.rolldisable();
}
key=dice.Roll();
count++;
String toScreen= "Number of rolls: "+count;
display.setText(toScreen);
text1.setText(""+key);
check = player1.Check(list,key);
if(check < 0)
{
count1++;
for(int i=1;i<7;i++)
{
if(key == i)
{
list[i] = key;
checkflag[i]=true;
fields[i].setText(""+key);
if(checkflag[i] == true)
{
flag = true;
for(int a=1; a<7; a++)
{
if(checkflag[a]==false)
{
flag=false;
}
}
}
}
if(flag == true)
{
display.setText("Congratulation, you have \ntaken "+count+" rolls to get one of \neach number between 1 and 6");
rolldisable();
restartButton.setVisible(true);
}
}
}
if(event.getSource() == restartButton)
{
restart();
}
}
public void rollenable()
{
roll.setEnabled(true);
}
public void rolldisable()
{
roll.setEnabled(false);
}
public JButton getSubButton()
{
return this.roll;
}
}
Player.java
import java.util.*;
import javax.swing.*;
public class Player
{
private final int MAX = 100;
private Die play = new Die();
private boolean[] results = {false,false,false,false,false,false,false};
private int count;
private int key;
private boolean check = false;
//private JButton roll;
public Player()
{
count=0;
key=0;
}
public void Play()
{
for(int i=0;i<MAX;i++)
{
key=play.Roll();
System.out.print("\nNumber rolled: "+key);
count++;
for(int a=1;a<7;a++)
{
if(a==key)
{
results[a]=true;
System.out.print("\nSo far, you have rolled ");
if(results[a]==true)
{
check=true;
for(int k=1;k<7;k++)
{
if(results[k] ==true)
{
System.out.print(" "+k+" ");
}
if(results[k] == false)
check=false;
}
}
}
}
if(check==true)
{
System.out.print("\nCongratulations, you have taken "+count+" rolls to get one of each number between 1 and 6");
break;
}
}
}
public int Check(int []check,int key)
{
int check1= Arrays.binarySearch(check,key);
return check1;
}
//public JButton getButton()
//{
// JButton button = null;
//SixNumbersPanel2 roll = new SixNumbersPanel2();
//return roll.roll = button;
//}
public void rolldisable()
{
SixNumbersPanel2 dis = new SixNumbersPanel2();
dis.getSubButton().setEnabled(false);
}
}
Also with the restartButton JButton, it doesn't restart the whole game but only for the player who clicks it. Any suggestion as how to make it restart the whole GUI would be awesome.
All helps would be appreciated.
This...
public void rolldisable()
{
SixNumbersPanel2 dis = new SixNumbersPanel2();
dis.getSubButton().setEnabled(false);
}
is wrong, basically you're creating a new instance of SixNumbersPanel2 and are trying to change it's state, but it's not actually displayed on the screen nor does it have anything to do with what is displayed on the screen.
This solution is not a simple one. What you need is some kind of model/controller which can be shared between the two instances of SixNumbersPanel2 and which generate appropriate state events to which they can respond.
You will need to devise some way of identifying which player is which so each SixNumbersPanel2 knows which player it represents and which player is currently active
Updated...
In order to implement the function changes needed, you need some kind of "central" hub, which is managing the core functionality and which can generate notification/events when the state changes in some meaningful way (like the next players turn)
In broad terms, these are covered by:
Model-View-Controller
Observer Patten
First, we need to start with some kind of token, to differentiate the players...
enum Player {
ONE, TWO;
}
Next, we need some kind of "model", which is used to manage the data and the core functionality
public interface SixNumbersModel {
public Player getCurrentTurn();
public Player nextTurn();
public int roll();
public boolean hasWon(Player player);
public Set<Integer> getPlayerResults(Player player);
public int getTurnsCount(Player player);
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}
nb: You could actually have a "player model" which managed the results, but I'm been lazy
The reason I've started with a interface is users of the model should not care about how the model is implemented (implementations might seperate the management of each players results into individual models, but users of the SixNumbersModel won't care)
public class DefaultSizeNumbersModel implements SixNumbersModel {
private List<ChangeListener> changeListeners;
private Die die = new Die();
private Player turn;
private Map<Player, Set<Integer>> results;
private Map<Player, Integer> turns;
public DefaultSizeNumbersModel() {
changeListeners = new ArrayList<>(2);
results = new HashMap<>();
results.put(Player.ONE, new HashSet<>(6));
results.put(Player.TWO, new HashSet<>(6));
turns = new HashMap<>(2);
turns.put(Player.ONE, 0);
turns.put(Player.TWO, 0);
setCurrentTurn(Player.ONE);
}
#Override
public Player getCurrentTurn() {
return turn;
}
protected void setCurrentTurn(Player player) {
turn = player;
}
#Override
public Player nextTurn() {
switch (getCurrentTurn()) {
case ONE:
setCurrentTurn(Player.TWO);
break;
case TWO:
setCurrentTurn(Player.ONE);
break;
default:
setCurrentTurn(Player.ONE);
break;
}
fireStateChanged();
return getCurrentTurn();
}
#Override
public int roll() {
incrementTurnCount(getCurrentTurn());
int result = die.Roll();
Set<Integer> playerResults = results.get(getCurrentTurn());
playerResults.add(result);
return result;
}
#Override
public boolean hasWon(Player player) {
Set<Integer> playerResults = results.get(getCurrentTurn());
return playerResults.size() == 5; // 0...5
}
#Override
public Set<Integer> getPlayerResults(Player player) {
Set<Integer> actualResults = results.get(player);
Set<Integer> copy = new HashSet<>(actualResults);
return copy;
}
#Override
public int getTurnsCount(Player player) {
return turns.get(player);
}
protected void setTurnsCount(Player player, int count) {
turns.put(player, count);
}
protected void incrementTurnCount(Player player) {
int count = getTurnsCount(player);
count++;
setTurnsCount(player, count);
}
#Override
public void addChangeListener(ChangeListener listener) {
changeListeners.add(listener);
}
#Override
public void removeChangeListener(ChangeListener listener) {
changeListeners.remove(listener);
}
protected void fireStateChanged() {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : changeListeners) {
listener.stateChanged(evt);
}
}
}
Then we need some kind of "view"...
public class SixNumbersPanel extends JPanel {
private Player player;
private SixNumbersModel model;
private JButton roll;
private JTextArea ta;
public SixNumbersPanel(Player player, SixNumbersModel model) {
this.player = player;
this.model = model;
model.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
roll.setEnabled(player == model.getCurrentTurn());
}
});
roll = new JButton("Roll");
ta = new JTextArea(5, 10);
roll.setEnabled(player == model.getCurrentTurn());
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JLabel(player.name()), gbc);
add(roll, gbc);
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(new JScrollPane(ta), gbc);
roll.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int result = model.roll();
ta.append(Integer.toString(result) + "\n");
if (model.hasWon(player)) {
JOptionPane.showMessageDialog(SixNumbersPanel.this, player + " has won");
}
model.nextTurn();
}
});
}
}
Okay, massively basic, but, it simply has a button and a text area. It registers interest to the model to be notified when the state changes and makes sure that the button is only enabled when the player it represents is the current player.
Runnable example...
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
enum Player {
ONE, TWO;
}
public interface SixNumbersModel {
public Player getCurrentTurn();
public Player nextTurn();
public int roll();
public boolean hasWon(Player player);
public Set<Integer> getPlayerResults(Player player);
public int getTurnsCount(Player player);
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}
public class DefaultSizeNumbersModel implements SixNumbersModel {
private List<ChangeListener> changeListeners;
private Die die = new Die();
private Player turn;
private Map<Player, Set<Integer>> results;
private Map<Player, Integer> turns;
public DefaultSizeNumbersModel() {
changeListeners = new ArrayList<>(2);
results = new HashMap<>();
results.put(Player.ONE, new HashSet<>(6));
results.put(Player.TWO, new HashSet<>(6));
turns = new HashMap<>(2);
turns.put(Player.ONE, 0);
turns.put(Player.TWO, 0);
setCurrentTurn(Player.ONE);
}
#Override
public Player getCurrentTurn() {
return turn;
}
protected void setCurrentTurn(Player player) {
turn = player;
}
#Override
public Player nextTurn() {
switch (getCurrentTurn()) {
case ONE:
setCurrentTurn(Player.TWO);
break;
case TWO:
setCurrentTurn(Player.ONE);
break;
default:
setCurrentTurn(Player.ONE);
break;
}
fireStateChanged();
return getCurrentTurn();
}
#Override
public int roll() {
incrementTurnCount(getCurrentTurn());
int result = die.Roll();
Set<Integer> playerResults = results.get(getCurrentTurn());
playerResults.add(result);
return result;
}
#Override
public boolean hasWon(Player player) {
Set<Integer> playerResults = results.get(getCurrentTurn());
return playerResults.size() == 5; // 0...5
}
#Override
public Set<Integer> getPlayerResults(Player player) {
Set<Integer> actualResults = results.get(player);
Set<Integer> copy = new HashSet<>(actualResults);
return copy;
}
#Override
public int getTurnsCount(Player player) {
return turns.get(player);
}
protected void setTurnsCount(Player player, int count) {
turns.put(player, count);
}
protected void incrementTurnCount(Player player) {
int count = getTurnsCount(player);
count++;
setTurnsCount(player, count);
}
#Override
public void addChangeListener(ChangeListener listener) {
changeListeners.add(listener);
}
#Override
public void removeChangeListener(ChangeListener listener) {
changeListeners.remove(listener);
}
protected void fireStateChanged() {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : changeListeners) {
listener.stateChanged(evt);
}
}
}
public class Die {
private final int MAX = 6;
private int die1;
Random rand = new Random();
//Constructor
public Die() {
die1 = 1;
}// end Constructor
public int Roll() {
die1 = rand.nextInt(MAX) + 1;
return die1;
}
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Roll Six");
frame.setLayout(new GridLayout(2, 0));
SixNumbersModel model = new DefaultSizeNumbersModel();
SixNumbersPanel onePane = new SixNumbersPanel(Player.ONE, model);
SixNumbersPanel twoPane = new SixNumbersPanel(Player.TWO, model);
frame.add(onePane);
frame.add(twoPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class SixNumbersPanel extends JPanel {
private Player player;
private SixNumbersModel model;
private JButton roll;
private JTextArea ta;
public SixNumbersPanel(Player player, SixNumbersModel model) {
this.player = player;
this.model = model;
model.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
roll.setEnabled(player == model.getCurrentTurn());
}
});
roll = new JButton("Roll");
ta = new JTextArea(5, 10);
roll.setEnabled(player == model.getCurrentTurn());
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JLabel(player.name()), gbc);
add(roll, gbc);
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(new JScrollPane(ta), gbc);
roll.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int result = model.roll();
ta.append(Integer.toString(result) + "\n");
if (model.hasWon(player)) {
JOptionPane.showMessageDialog(SixNumbersPanel.this, player + " has won");
}
model.nextTurn();
}
});
}
}
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm developing a java component for my sudoku project. This component represents a sudoku box that can have two states: a digit or candidates (I use a CardLayout). So far, all works. I am now trying to add some MouseListener on my Labels. When I click on a candidate, I would like to change the value of the state with a simple number, then change the display (cardlayout.show (...)). The problem here is that the source of the event seems to be detected, but the change is not done ... Here is my code (Problem in CaseComponent > createController > candidate[i].addMouseListener():
package sudoku;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CaseComponent extends JPanel {
private static final long serialVersionUID = 1L;
private static final String CANDIDATES = "candidate";
private static final String DIGIT = "digit";
private GridBagConstraints gridBagConstraint;
private JLabel[] candidates;
private JLabel digit;
public CaseComponent(Color caseColor){
super();
setLayout(new CardLayout());
setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
setPreferredSize(new Dimension(60, 60));
setBackground(caseColor);
createView();
placeComponents();
createController();
}
private void createView() {
gridBagConstraint = new GridBagConstraints();
candidates = new JLabel[9];
for (int i = 0; i < 9; i++) {
candidates[i] = new JLabel(""+(i + 1));
}
digit = new JLabel();
}
private void placeComponents() {
JPanel p = new JPanel(new GridLayout(3, 3)); {
for (int i = 0; i < 9; i++) {
JPanel q = new JPanel(); {
q.add(candidates[i]);
}
q.setOpaque(false);
p.add(q);
}
}
p.setOpaque(false);
add(p, DIGIT);
p = new JPanel(new GridBagLayout()); {
p.add(digit, gridBagConstraint);
p.setOpaque(false);
}
add(p, CANDIDATES);
}
private void createController() {
digit.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
candidateDisplay();
}
}
});
for (int i = 0; i < 9; i++) {
candidates[i].addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JLabel source = (JLabel) e.getSource();
if (SwingUtilities.isLeftMouseButton(e)) {
System.out.println("IN");
digitDisplay();
digit.setText(source.getText());
}
}
});
}
}
private void digitDisplay() {
CardLayout cl = (CardLayout) getLayout();
cl.show(this, DIGIT);
}
private void candidateDisplay() {
CardLayout cl = (CardLayout) getLayout();
cl.show(this, CANDIDATES);
}
}
package sudoku;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Sudoku {
// ATTRIBUTS
private JFrame mainFrame;
private Timer timer;
private JButton pauseButton;
private JButton resumeButton;
private JButton penaltyButton;
private JLabel chronoInfo;
private Chronometer chrono;
public Sudoku() {
createModel();
createView();
placeComponents();
createController();
}
// COMMANDES
/**
* Rend l'application visible au centre de l'écran.
*/
public void display() {
mainFrame.pack();
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
}
// OUTILS
private void createModel() {
chrono = new Chronometer();
}
private void createView() {
mainFrame = new JFrame("Sudoku");
resumeButton = new JButton("Reprendre");
resumeButton.setEnabled(false);
pauseButton = new JButton("Pause");
penaltyButton = new JButton("Penalité");
chronoInfo = new JLabel("00:00");
}
private void placeComponents() {
JPanel p = new JPanel(); {
p.add(resumeButton);
p.add(pauseButton);
p.add(penaltyButton);
}
mainFrame.add(p, BorderLayout.NORTH);
p = new JPanel(); {
p.add(chronoInfo);
}
mainFrame.add(p, BorderLayout.SOUTH);
p = new JPanel(); {
JPanel q = new JPanel(new GridLayout(3,3)); {
for (int i = 0; i < 9; i++) {
Color caseColor = i % 2 == 0 ? Color.WHITE : Color.GRAY;
JPanel r = new JPanel(new GridLayout(3,3)); {
for (int j = 0; j < 9; j++) {
r.add(new CaseComponent(caseColor));
}
}
r.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
q.add(r);
}
}
q.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
p.add(q);
}
mainFrame.add(p);
}
private void createController() {
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final int delay = 1000;
timer = new Timer(delay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
chrono.increment();
chronoInfo.setText(chrono.toString());
}
});
timer.start();
resumeButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
chrono.setPaused(false);
}
});
pauseButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timer.stop();
chrono.setPaused(true);
}
});
penaltyButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
chrono.penalty();
chronoInfo.setText(chrono.toString());
}
});
chrono.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
resumeButton.setEnabled(chrono.isPaused());
pauseButton.setEnabled(!chrono.isPaused());
}
});
}
// POINT D'ENTREE
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Sudoku().display();
}
});
}
}
package sudoku;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
public class Chronometer {
// ATTRIBUTS
private static final int MAX_SECONDS = 60;
private final EventListenerList listeners;
private final ChangeEvent changeEvent;
private int seconds;
private int minutes;
private boolean paused;
// CONSTRUCTEUR
public Chronometer() {
listeners = new EventListenerList();
changeEvent = new ChangeEvent(this);
seconds = 0;
minutes = 0;
paused = false;
}
// REQUETES
public int getSeconds() {
return seconds;
}
public int getMinutes() {
return minutes;
}
public boolean isPaused() {
return paused;
}
public ChangeListener[] getChangeListeners() {
return listeners.getListeners(ChangeListener.class);
}
// COMMANDES
public void setSeconds(int s) {
seconds = s;
}
public void setMinutes(int m) {
minutes = m;
}
public void setPaused(boolean b) {
paused = b;
fireStateChanged();
}
public void increment() {
seconds++;
if (seconds == MAX_SECONDS) {
seconds = 0;
minutes++;
}
}
public void penalty() {
seconds += 10;
if (seconds >= MAX_SECONDS) {
minutes++;
seconds -= MAX_SECONDS;
}
}
public void reset() {
seconds = 0;
minutes = 0;
fireStateChanged();
}
public void addChangeListener(ChangeListener listener) {
if (listener == null) {
return;
}
listeners.add(ChangeListener.class, listener);
}
public void removeChangeListener(ChangeListener listener) {
if (listener == null) {
return;
}
listeners.remove(ChangeListener.class, listener);
}
public String toString() {
return String.format("%02d:%02d", minutes, seconds);
}
// OUTILS
protected void fireStateChanged() {
ChangeListener[] listnrs = getChangeListeners();
for (ChangeListener lst : listnrs) {
lst.stateChanged(changeEvent);
}
}
}
I thank you in advance !
Now that looks like a nice program! I took a quick look and I believe I found the problem. In your placeComponents method you swapped the candidates and digit keys of your layout. I also remove unnecessary brackets.
private void placeComponents() {
JPanel p = new JPanel(new GridLayout(3, 3));
for (int i = 0; i < 9; i++) {
JPanel q = new JPanel(); {
q.add(candidates[i]);
}
q.setOpaque(false);
p.add(q);
}
p.setOpaque(false);
add(p, CANDIDATES); // was DIGIT
p = new JPanel(new GridBagLayout()); {
p.add(digit, gridBagConstraint);
p.setOpaque(false);
}
add(p, DIGIT); // was CANDIDATES
}
I currently have multiples of a JPanel (ItemPanel) within a JPanel (FlipPanel) within my JFrame. An ItemPanel is added to FlipPanel when a JButton in a separate panel (FlipFormPanel) is pressed. I have a JButton on ItemPanel labelled "Cancel" and I would like to remove the specific ItemPanel whose Cancel JButton was pressed. What I currently have only removes the ItemPanel whose button was selected, but after that no other buttons will remove any more ItemPanels.
How can I fix my code to do what I'm trying to accomplish?
My code for each class is below. I tried to remove as much of the extraneous material as possible to avoid cluttering the task at hand. I believe the issue is located in FlipPanel under the methods addItemPanel(, setItemListener(, and in the ItemPanelListener and ItemPanelEvent classes. Thanks for any help provided.
FlipPanel:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import net.miginfocom.swing.MigLayout;
public class FlipPanel extends JPanel {
private JPanel mainPanel;
private JScrollPane scrollPane;
private ItemPanel itemPanel;
private JPanel titlePanel;
private JLabel titleLabel;
private List<ItemPanel> itmPnls = new ArrayList<ItemPanel>();
private int count;
public FlipPanel() {
setLayout(new BorderLayout());
mainPanel = new JPanel();
scrollPane = new JScrollPane(mainPanel);
add(scrollPane, BorderLayout.CENTER);
mainPanel.setLayout(new MigLayout("align center, fillx", "fill", ""));
titlePanel = new JPanel();
mainPanel.add(titlePanel, "wrap, gapbottom 13, gaptop 7");
titlePanel.setLayout(new GridBagLayout());
titleLabel = new JLabel("CURRENT FLIPS");
Font labelFont = titleLabel.getFont();
titleLabel.setFont(new Font(labelFont.getName(), Font.BOLD, 25));
titlePanel.setBorder(BorderFactory.createMatteBorder(0, 0, 3, 0, Color.BLACK));
titlePanel.add(titleLabel);
count = 0;
}
public void addItemPanel(String item, String buyPrice, String sellPrice,
String quantity, String pcBuyPrice, String pcSellPrice) {
this.itemPanel = new ItemPanel(count, item, buyPrice, sellPrice, quantity,
pcBuyPrice, pcSellPrice);
mainPanel.add(itemPanel, "wrap");
itmPnls.add(itemPanel);
count++;
setItemListener(this.itemPanel);
}
public void setItemListener(ItemPanel iP) {
iP.setItemPanelListener(new ItemPanelListener() {
public void itemPanelEventOccurred(ItemPanelEvent e) {;
System.out.println("Remove");
System.out.println("Current Position: " + e.getPosition());
mainPanel.remove(itmPnls.get(e.getPosition()));
System.out.println("Positions:");
for (int i = 0; i < count; i++) {
System.out.println(itmPnls.get(i).getPosition());
}
if(e.getPosition() < count - 1) {
for (int i = e.getPosition() + 1; i < count; i++) {
itmPnls.get(i).setPosition(i);
}
}
count--;
revalidate();
repaint();
System.out.println("Positions:");
for (int i = 0; i < count; i++) {
System.out.println(itmPnls.get(i).getPosition());
}
setItemListener(itmPnls.get(0));
}
});
}
ItemPanel:
import javax.swing.JPanel;
public class ItemPanel extends JPanel {
private int position;
private String item;
private String buyPrice;
private String sellPrice;
private String quantity;
private String pcBuyPrice;
private String pcSellPrice;
private JButton logBtn;
private JButton editBtn;
private JButton cancelBtn;
private ItemPanelListener itemPanelListener;
public ItemPanel(int position, String item, String buyPrice, String sellPrice,
String quantity, String pcBuyPrice, String pcSellPrice) {
this.position = position;
this.item = item;
this.buyPrice = buyPrice;
this.sellPrice = sellPrice;
this.quantity = quantity;
this.pcBuyPrice = pcBuyPrice;
this.pcSellPrice = pcSellPrice;
Dimension dim = getPreferredSize();
dim.height = 100;
setPreferredSize(dim);
setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
logBtn = new JButton("Log Item");
editBtn = new JButton("Edit Item");
cancelBtn = new JButton("Cancel Item");
setupLabels();
setupCancelItemButton();
layoutComponents();
}
public int getPosition() {
return position;
}
public void setPosition(int pos) {
position = pos;
}
public void setupCancelItemButton() {
cancelBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ItemPanelEvent ev = new ItemPanelEvent(this);
if (itemPanelListener != null) {
itemPanelListener.itemPanelEventOccurred(ev);
}
}
});
}
public void setItemPanelListener(ItemPanelListener listener) {
this.itemPanelListener = listener;
}
ItemPanelListener:
import java.util.EventListener;
public interface ItemPanelListener extends EventListener {
public void itemPanelEventOccurred(ItemPanelEvent e);
}
ItemPanelEvent:
import java.util.EventObject;
public class ItemPanelEvent extends EventObject {
private int position;
public ItemPanelEvent(Object source) {
super(source);
}
public ItemPanelEvent(Object source, int position) {
super(source);
this.position = position;
}
public int getPosition() {
return position;
}
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Alright, so, I am trying to make a game using a JFrame that, when you click a button, adds money, and show update some text(a JLabel).
You see, it should just update the Windows.Money variable and display ONLY the new variable on the screen, but, it adds more buttons. Please note: The money part works fine.
It's just that Java doesn't want to replace, only add.
Code:
package dev.bobdabiulder.classic.main;
import javax.swing.JLabel;
public class Bucket extends Window{
private static final long serialVersionUID = 1L;
public Bucket() throws InterruptedException {
for(int i = 0; !Window.Paused; ) {
Window.Money += 2;
this.wait(1000);
}
Window.Buckets++;
Window.BucketCounter = new JLabel("You have: " + Window.Buckets + " buckets!");
}
}
In the Windows class...
package dev.bobdabiulder.classic.main;
import java.awt.Button;
import java.awt.Component;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Window extends JFrame{
public static long Money = 0;
private static final long serialVersionUID = 1L;
private static Thread t1 = new Thread();
private static Thread t2 = new Thread();
private static Thread t3 = new Thread();
private static Thread t4 = new Thread();
private static Thread t5 = new Thread();
public JButton bucket = new JButton("Buy a Bucket!");
public JButton add$ = new JButton("Add Money!");
public JLabel money = new JLabel(Money + "");
public static long Buckets = 0;
public static JLabel BucketCounter = new JLabel("You have: " + Buckets + " buckets!");
public static JPanel buck = new JPanel();
public static boolean Paused = false;
static JFrame jf = new JFrame("Lol!");
//Window Method
public Window() {
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
buck.add(bucket);
buck.add(money);
buck.add(add$);
buck.add(BucketCounter);
jf.setSize(500, 500);
jf.add(buck);
bucket.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(bucket)) {
try {
new Bucket();
} catch (Exception e1) {
e1.printStackTrace();
}
}
System.out.println("Action Performed!");
}
});
pack();
}
//End of ActionPerformed
//Start of start()
public static void start() {
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
//
//
//
#SuppressWarnings("deprecation")
public static void stop() {
t1.stop();
t2.stop();
t3.stop();
t4.stop();
t5.stop();
}
}
I made some requested edits, and I get errors where I put *, the errors all read:
Cannot make a static reference to the non-static field Window.(anything),
Error Spots:
for(int i = 0; !*Window.Paused; ) {
*Window.Money += 2;
this.wait(1000);
}
*Window.Buckets++;
*Window.BucketCounter = new JLabel("You have: " + *Window.Buckets + " buckets!");
Brief example of MVC, well really Model-View. What this does is uses a Swing Timer and not Threads (not directly that is) to increment a JLabel held in a different class. It also uses PropertyChangeListener and support to notify the view (the GUI) of changes in the state of the model.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
/**
* http://stackoverflow.com/q/22620807/522444
* http://stackoverflow.com/a/22621767/522444
* #author Pete
*
*/
#SuppressWarnings("serial")
public class ShortMvc extends JPanel {
private JTextField moneyField = new JTextField(10);
private JTextField bucketField = new JTextField(10);
private MoneyModel model = new MoneyModel();
private Timer timer = new Timer(model.getTimerDelay(), new TimerListener());
private JButton moneyButton = new JButton("Add Money");
private JButton bucketButton = new JButton("Add Bucket");
public ShortMvc() {
moneyField.setEditable(false);
moneyField.setFocusable(false);
bucketField.setEditable(false);
bucketField.setFocusable(false);
bucketField.setText(String.valueOf(model.getBuckets()));
add(new JLabel("Money:"));
add(moneyField);
add(moneyButton);
add(new JLabel("Buckets:"));
add(bucketField);
add(bucketButton);
moneyButton.getModel().addChangeListener(new MoneyBtnModelListener());
moneyButton.setMnemonic(KeyEvent.VK_M);
bucketButton.addActionListener(new BucketButtonListener());
bucketButton.setMnemonic(KeyEvent.VK_B);
model.addPropertyChangeListener(new ModelListener());
timer.setInitialDelay(0);
}
private class BucketButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
model.incrementBuckets();
}
}
private class MoneyBtnModelListener implements ChangeListener {
private boolean pressed = false;
#Override
public void stateChanged(ChangeEvent e) {
ButtonModel model = (ButtonModel) e.getSource();
if (pressed == model.isPressed()) {
return;
}
pressed = model.isPressed();
if (pressed) {
timer.start();
} else {
timer.stop();
}
}
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
model.incrementMoney(model.getMoneyIncrementAmount());
}
}
private class ModelListener implements PropertyChangeListener {
private NumberFormat moneyFormat = NumberFormat.getCurrencyInstance();
public ModelListener() {
moneyField.setText(moneyFormat.format(model.getMoney()));
}
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (MoneyModel.MONEY.equals(pcEvt.getPropertyName())) {
moneyField.setText(moneyFormat.format(model.getMoney()));
}
else if (MoneyModel.BUCKETS.equals(pcEvt.getPropertyName())) {
int buckets = model.getBuckets();
bucketField.setText(String.valueOf(buckets));
timer.setDelay(model.getTimerDelay());
}
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Short Mvc");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ShortMvc());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class MoneyModel {
public static final String MONEY = "money";
public static final String BUCKETS = "buckets";
private static final int INIT_TIMER_DELAY = 500;
public static final long MONEY_INCREMENT_AMOUNT = 2L;
private long money = 0L;
private int buckets = 1;
private int timerDelay = INIT_TIMER_DELAY;
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(
this);
public void setMoney(long money) {
long oldValue = this.money;
long newValue = money;
this.money = money;
pcSupport.firePropertyChange(MONEY, oldValue, newValue);
}
public long getMoneyIncrementAmount() {
return MONEY_INCREMENT_AMOUNT;
}
public void incrementMoney(long addToMoney) {
long oldValue = this.money;
long newValue = money + addToMoney;
this.money = newValue;
pcSupport.firePropertyChange(MONEY, oldValue, newValue);
}
public long getMoney() {
return money;
}
public void setBuckets(int buckets) {
int oldValue = this.buckets;
int newValue = buckets;
this.buckets = newValue;
timerDelay = INIT_TIMER_DELAY / buckets;
pcSupport.firePropertyChange(BUCKETS, oldValue, newValue);
}
public void incrementBuckets(int incrementAmount) {
int newValue = this.buckets + incrementAmount;
setBuckets(newValue);
}
// increment by one
public void incrementBuckets() {
incrementBuckets(1);
}
public int getBuckets() {
return buckets;
}
public int getTimerDelay() {
return timerDelay;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
}
Note that I can't use an ActionListener for this to work since an ActionListener only gets activated when the button is released. I'm assuming that you want to accumulate money when the button is pressed, and then stop accumulating when it is released. To do this, you must extract the JButton's model, add a ChangeListener to it, and then react to changes to the model's isPressed() method. I use it to start and stop the Swing Timer that increments the model.
Edit
Next iteration with better MVC (model-view-control) separation:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
import javax.swing.text.JTextComponent;
/**
* http://stackoverflow.com/q/22620807/522444
* http://stackoverflow.com/a/22621767/522444
* #author Pete
*
*/
public class ShortMvc {
private static void createAndShowGui() {
ShortView view = new ShortView();
MoneyModel model = new MoneyModel();
ShortControl control = new ShortControl(model, view);
control.init();
JFrame frame = new JFrame("Short MVC");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(view.getMainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class ShortView {
private JTextField moneyField = new JTextField(10);
private JTextField bucketField = new JTextField(10);
private JButton moneyButton = new JButton();
private JButton bucketButton = new JButton();
private JPanel mainPanel = new JPanel();
public ShortView() {
moneyField.setEditable(false);
moneyField.setFocusable(false);
bucketField.setEditable(false);
bucketField.setFocusable(false);
mainPanel.add(new JLabel("Money:"));
mainPanel.add(moneyField);
mainPanel.add(moneyButton);
mainPanel.add(new JLabel("Buckets:"));
mainPanel.add(bucketField);
mainPanel.add(bucketButton);
}
public JComponent getMainPanel() {
return mainPanel;
}
public JTextComponent getMoneyField() {
return moneyField;
}
public JTextComponent getBucketField() {
return bucketField;
}
public AbstractButton getMoneyButton() {
return moneyButton;
}
public AbstractButton getBucketButton() {
return bucketButton;
}
}
#SuppressWarnings("serial")
class ShortControl {
private MoneyModel model;
private ShortView view;
private Timer timer;
private MoneyBtnAction moneyBtnAction = new MoneyBtnAction("Add Money", KeyEvent.VK_M);
private BucketButtonAction bucketAction = new BucketButtonAction("Add Buckets", KeyEvent.VK_B);
public ShortControl(MoneyModel model, ShortView view) {
this.model = model;
this.view = view;
timer = new Timer(model.getTimerDelay(), new TimerListener());
}
public void init() {
timer.setInitialDelay(0);
view.getBucketField().setText(String.valueOf(model.getBuckets()));
view.getMoneyButton().setAction(moneyBtnAction);
view.getMoneyButton().getModel().addChangeListener(moneyBtnAction);
view.getBucketButton().setAction(bucketAction);
model.addPropertyChangeListener(new ModelListener());
}
private class BucketButtonAction extends AbstractAction {
public BucketButtonAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
model.incrementBuckets();
}
}
private class MoneyBtnAction extends AbstractAction implements ChangeListener {
private boolean pressed = false;
public MoneyBtnAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// empty
}
#Override
public void stateChanged(ChangeEvent e) {
ButtonModel model = (ButtonModel) e.getSource();
if (pressed == model.isPressed()) {
return;
}
pressed = model.isPressed();
if (pressed) {
timer.start();
} else {
timer.stop();
}
}
}
private class ModelListener implements PropertyChangeListener {
private NumberFormat moneyFormat = NumberFormat.getCurrencyInstance();
public ModelListener() {
view.getMoneyField().setText(moneyFormat.format(model.getMoney()));
}
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (MoneyModel.MONEY.equals(pcEvt.getPropertyName())) {
view.getMoneyField().setText(moneyFormat.format(model.getMoney()));
}
else if (MoneyModel.BUCKETS.equals(pcEvt.getPropertyName())) {
int buckets = model.getBuckets();
view.getBucketField().setText(String.valueOf(buckets));
timer.setDelay(model.getTimerDelay());
}
}
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
model.incrementMoney(model.getMoneyIncrementAmount());
}
}
}
class MoneyModel {
public static final String MONEY = "money";
public static final String BUCKETS = "buckets";
private static final int INIT_TIMER_DELAY = 500;
public static final long MONEY_INCREMENT_AMOUNT = 2L;
private long money = 0L;
private int buckets = 1;
private int timerDelay = INIT_TIMER_DELAY;
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(
this);
public void setMoney(long money) {
long oldValue = this.money;
long newValue = money;
this.money = money;
pcSupport.firePropertyChange(MONEY, oldValue, newValue);
}
public long getMoneyIncrementAmount() {
return MONEY_INCREMENT_AMOUNT;
}
public void incrementMoney(long addToMoney) {
long oldValue = this.money;
long newValue = money + addToMoney;
this.money = newValue;
pcSupport.firePropertyChange(MONEY, oldValue, newValue);
}
public long getMoney() {
return money;
}
public void setBuckets(int buckets) {
int oldValue = this.buckets;
int newValue = buckets;
this.buckets = newValue;
timerDelay = INIT_TIMER_DELAY / buckets;
pcSupport.firePropertyChange(BUCKETS, oldValue, newValue);
}
public void incrementBuckets(int incrementAmount) {
int newValue = this.buckets + incrementAmount;
setBuckets(newValue);
}
// increment by one
public void incrementBuckets() {
incrementBuckets(1);
}
public int getBuckets() {
return buckets;
}
public int getTimerDelay() {
return timerDelay;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
}
Ok, this should help a bit... missing Timer but you can get an idea of how to manage ActionListeners:
Main class
public class WindowInit{
public static void main(String args[]){
WindowHelp wh = new WindowHelp();
}
}
WindowHelp (This one does the magic)
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
import javax.swing.event.*;
public class WindowHelp{
JButton b1;
JButton b2;
JLabel label;
long money;
long buckets;
JFrame jf;
Timer timer;
public WindowHelp(){
money = 0;
buckets = 0;
b1 = new JButton("Add buckets");
b2 = new JButton("Add money");
label = new JLabel("");
jf = new JFrame("My Game");
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
buckets += 2;
label.setText("Buckets: " + buckets);
}
});
b1.setBounds(50,50,100,30);
b2.setBounds(200,50,100,30);
label.setBounds(300,50,200,30);
jf.add(b1);
jf.add(b2);
jf.add(label);
jf.setSize(500,600);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Yes, one is the main class you compile "WindowInit" and run that one.
For timers, gimme some time while I implement it.
I'm making a piano application in Java. This is one of the functions,
public void playOnce(int time) {
play();
doClick(time);
stop();
}
public void play() {
channel[0].noteOn(note, 60);
}
public void stop() {
channel[0].noteOff(note);
}
I'll provide a minimal working example if necessary, but I wanted to make sure it's not an obvious issue. The problem is that playOnce is called in a while loop. playOnce is in a Key class, and each Key has a different note. In each iteration of the while loop, playOnce is called on a different key. Once all the keys have been played, it stops.
The doClick method correctly pressed the key, but it's not released until all the keys have been played. In fact, while the keys are being played, you can't do anything, even press the pause button. For this problem, I guess I could put the entire loop in a different thread, but I don't think that type of solution will allow the key to be released.
EDIT: Yea, I figured out I need a new thread to get other actions to work, but I still need a fix for doClick(). This might be more complicated than I thought so here's a working example,
Main.java
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.DecimalFormat;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.SpinnerNumberModel;
public class Main implements ActionListener {
final int WHITE_KEY_WIDTH, WHITE_KEY_HEIGHT, BLACK_KEY_WIDTH,
BLACK_KEY_HEIGHT;
final int WIDTH;
final JFileChooser fc;
{
WHITE_KEY_WIDTH = Key.WHITE_KEY_WIDTH;
BLACK_KEY_WIDTH = Key.BLACK_KEY_WIDTH;
WHITE_KEY_HEIGHT = Key.WHITE_KEY_HEIGHT;
BLACK_KEY_HEIGHT = Key.BLACK_KEY_HEIGHT;
WIDTH = 3 * (WHITE_KEY_WIDTH * 7) + WHITE_KEY_WIDTH;
fc = new JFileChooser();
}
public static Key keys[] = new Key[48];
private static int index = 0;
private String prevText = "";
JTextArea shabadEditor = null;
JSpinner tempoControl;
JFrame frame;
File curFile;
public static void main(String[] args) {
new Main();
}
public Main() {
frame = new JFrame();
JPanel mainPanel = new JPanel();
JPanel controlPanel = new JPanel();
JLayeredPane pianoPanel = new JLayeredPane();
mainPanel.setLayout(new GridBagLayout());
JButton playButton = new JButton("Play");
JButton pauseButton = new JButton("Pause");
playButton.addActionListener(this);
playButton.setActionCommand("play");
pauseButton.addActionListener(this);
pauseButton.setActionCommand("pause");
SpinnerNumberModel model = new SpinnerNumberModel(1, 0, 2, .1);
tempoControl = new JSpinner(model);
JSpinner.NumberEditor editor = (JSpinner.NumberEditor) tempoControl
.getEditor();
DecimalFormat format = editor.getFormat();
format.setMinimumFractionDigits(1);
Dimension d = tempoControl.getPreferredSize();
d.width = 40;
tempoControl.setPreferredSize(d);
GridBagConstraints c = new GridBagConstraints();
// Construct each top level component
controlPanel.add(playButton);
controlPanel.add(pauseButton);
controlPanel.add(tempoControl);
shabadEditor = new JTextArea(20, 78);
constructKeyboard(pianoPanel);
// Add the piano panel and shabad editor to the window
c.gridx = 0;
c.gridy = 0;
c.weightx = 1.0;
c.anchor = GridBagConstraints.NORTHWEST;
mainPanel.add(controlPanel, c);
c.gridx = 0;
c.gridy = 1;
c.weightx = 1.0;
// c.weighty = 1.0;
c.anchor = GridBagConstraints.NORTHWEST;
pianoPanel
.setPreferredSize(new Dimension(WIDTH - 18, WHITE_KEY_HEIGHT));
mainPanel.add(pianoPanel, c);
c.gridx = 0;
c.gridy = 2;
c.weightx = 1.0;
c.weighty = 1.0;
c.anchor = GridBagConstraints.NORTHWEST;
mainPanel.add(shabadEditor, c);
frame.add(mainPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(WIDTH, WHITE_KEY_HEIGHT * 3 + 30);
frame.setLocation(250, 60);
frame.setVisible(true);
}
void constructKeyboard(Container panel) {
int i = 0;
int j = 0;
for (int k = 0; k < 3; k++) {
addWhiteKey(panel, i++);
addBlackKey(panel, j++);
addWhiteKey(panel, i++);
addBlackKey(panel, j++);
addWhiteKey(panel, i++);
addWhiteKey(panel, i++);
j++;
addBlackKey(panel, j++);
addWhiteKey(panel, i++);
addBlackKey(panel, j++);
addWhiteKey(panel, i++);
addBlackKey(panel, j++);
j++;
addWhiteKey(panel, i++);
}
}
void addWhiteKey(Container panel, int i) {
WhiteKey b = new WhiteKey();
b.setLocation(i++ * WHITE_KEY_WIDTH, 0);
panel.add(b, 0, -1);
keys[index++] = b;
}
void addBlackKey(Container panel, int factor) {
BlackKey b = new BlackKey();
b.setLocation(WHITE_KEY_WIDTH - BLACK_KEY_WIDTH / 2 + factor
* WHITE_KEY_WIDTH, 0);
panel.add(b, 1, -1);
keys[index++] = b;
}
#Override
public void actionPerformed(ActionEvent arg0) {
String action = arg0.getActionCommand();
if (action.equals("play")) {
System.out.println("working");
for (int i = 0; i < 10; i++) {
keys[i].playOnce(500);
}
}
}
}
Key.java
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;
import javax.swing.JButton;
public class Key extends JButton implements MouseListener {
private static final long serialVersionUID = 1L;
public static final int WHITE_KEY_HEIGHT = 200;
public static final int WHITE_KEY_WIDTH = 40;
public static final int BLACK_KEY_WIDTH = 20;
public static final int BLACK_KEY_HEIGHT = 120;
private static int noteCount = 40;
public int note;
private static Synthesizer synth = null;
static {
try {
synth = MidiSystem.getSynthesizer();
synth.open();
} catch (MidiUnavailableException e) {
e.printStackTrace();
}
}
MidiChannel channel[];
public Key() {
note = noteCount++;
// Instrument[] instruments = synth.getAvailableInstruments();
// for (Instrument instrument : instruments) {
// System.out.println(instrument.getName());
// System.out.println(instrument.getPatch().getBank());
// System.out.println(instrument.getPatch().getProgram());
// }
channel = synth.getChannels();
channel[0].programChange(20);
addMouseListener(this);
}
public void playOnce(int time) {
play();
doClick(time);
stop();
}
public void play() {
channel[0].noteOn(note, 60);
}
public void stop() {
channel[0].noteOff(note);
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println(this.note);
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
play();
}
#Override
public void mouseReleased(MouseEvent e) {
stop();
}
}
BlackKey.java
import java.awt.Color;
public class BlackKey extends Key {
private static final long serialVersionUID = 1L;
public BlackKey() {
super();
setBackground(Color.BLACK);
setSize(BLACK_KEY_WIDTH, BLACK_KEY_HEIGHT);
}
}
WhiteKey.java
import java.awt.Color;
public class WhiteKey extends Key {
private static final long serialVersionUID = 1L;
public WhiteKey() {
super();
setBackground(Color.WHITE);
setSize(WHITE_KEY_WIDTH, WHITE_KEY_HEIGHT);
}
}
EDIT: After doing a bit of work with threading, this is what I have
By putting the for loop in another thread, the keys are released at the right time:
#Override
public void actionPerformed(ActionEvent arg0) {
String action = arg0.getActionCommand();
if (action.equals("play")) {
System.out.println("working");
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 20; i++) {
keys[i].playOnce(100);
}
}
}).start();
}
}
}
The issue now is that the keyboard glitches. The keyboard is created using a layered pane, and for some reason when the keys are released the layers that are supposed to be on the bottom come to the top. When I hover my mouse over them, the glitch goes away. Any ideas?
EDIT2: I fixed the glitches. I simply had to add
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
after doClick();
The problem with your approach is that you are blocking the event thread. This thread is responsible for user input, painting and window updates. My guess is now, that doClick's timeout gets checked inside the event thread (seems logical), so it won't get released until your actionPerformed method exits (and so the event thread can continue its event processing).
A solution to this problem would be (as you already mentioned) moving your for loop to another thread and call doClick using SwingUtilities.invokeLater.