I've this class:
public class Gemme extends JButton implements ActionListener{
private static int indicateur=0;
private Gemme gemme1;
private Gemme gemme2;
#Override
public void actionPerformed(ActionEvent e) {
indicateur = 1-indicateur;
if(this.indicateur==1)//first click
{
this.gemme1 = (Gemme) e.getSource();
}
else
{
this.gemme2 = (Gemme) e.getSource();
switchColor();
}
}
private void switchColor()
{
Color c = gemme1.getBackground();
gemme1.setBackground(gemme2.getBackground());
gemme1.setBackground(c);
System.out.println("color switched");
}
Basically i'm creating a game:
A grid of Colored JButtons, when the player try to click on two JBs in order to switch color between them.
The problem that i get NPE at this line:
Color c = gemme1.getBackground();
But when i put the Color c = gemme1.getBackground(); in the actionPerformed like that:
public void actionPerformed(ActionEvent e) {
indicateur = 1-indicateur;
if(this.indicateur==1)//first click
{
this.gemme1 = (Gemme) e.getSource();
Color c = gemme1.getBackground();
}
else
{
this.gemme2 = (Gemme) e.getSource();
//switchColor();
}
}
It works, i didn't understand, is the JB is losing his properties when the action is not matching to it?
Any explanation please?
EDIT
I changed switchColor() to :
private void switchColor()
{
System.out.println(gemme1);
System.out.println(gemme2);
}
Output:
null
mini.projet.dev.game.components.Gemme[btn42,104,216,51x53,alignmentX=0.0,alignmentY=0.5,b...........
How gemme1 becomes null ?
I solved the problem, it was just changing gemme1/gemme2 to static in order to becomes shared between other instances of Gemme:
private static Gemme gemme1;
private static Gemme gemme2;
if(this.indicateur==1)//first click
{
this.gemme1 = (Gemme) e.getSource();
}
else
{
this.gemme2 = (Gemme) e.getSource();
switchColor();
}
In this code, you are initializing the gemme2 member only. When you call switchColor() in the else block, the gemme1 remains undefined, e.g. null. That's why you get NPE during the gemme1.getBackground(); call.
Related
I'm making a small game involving a grid of JButtons (MxN) and the main premise is to click on buttonA and then on buttonB, coloring buttonB and adjacent buttons of the same color as buttonB with the color of buttonA. I have made it so you are able to choose 3 possible difficulties. The colors are randomly generated. The main problem is getting the colors to change.
This is the method that I call after selecting the difficulty of the game:
public static void gameMechanics(int m, int n) {
final String[] pickedColour = {""};
final String[] placedColour = {""};
JButton[][] picked = new JButton[m][n];
JButton[][] placed = new JButton[m][n];
picked[m][n].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
pickedColour[0] = picked[m][n].getText();
}
});
placed[m][n].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
placedColour[0] = placed[m][n].getText();
}
});
if (pickedColour[0] == "R" && placedColour[0] != "R") {
placed[m][n].setBackground(Color.RED);
placed[m][n].setText("R");
}
else if (pickedColour[0] == "G" && placedColour[0] != "G") {
placed[m][n].setBackground(Color.GREEN);
placed[m][n].setText("G");
}
else if (pickedColour[0] == "B" && placedColour[0] != "B") {
placed[m][n].setBackground(Color.BLUE);
placed[m][n].setText("B");
}
}
I would consider using JPanels and painting them, using a MouseListener instead.
However, if you're set on using JButtons, try this:
button.setBackground(Color.GREEN);
button.setOpaque(true);
Note that this might not work if you're setting the look and feel using UIManager.
Also, you're doing a ton of extra work to map the color to the button - it could get confusing and cause errors down the road. Instead, you might try creating your own class:
class ColoredButton extends JButton {
private static final long serialVersionUID = 3040767030924461426L;
private Color color;
public ColoredButton(Color c) {
this.color = c;
this.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
changeColor();
}
});
}
public void changeColor() {
this.setBackground(this.color);
this.setOpaque(true);
}
}
Now, you can construct a new ColoredButton:
// Now, this button will turn green when clicked
ColoredButton temp = new ColoredButton(Color.GREEN);
I am currently trying to set up a Tic Tac Toe GUI grid with 3x3 buttons, which change the button icon to a nought or cross whenever pressed.
I have set up an array of JButtons and have added a listener to check whenever the button is pressed.
My problem is getting access to the Button object inside the array, I have had to add a parameter to my custom listener constructor to save the button object reference for when I want to change it. It works as is, but doesn't feel very elegant. Is there a way to find the correct JButton object in buttons while inside actionPerformed, or is there a better way to do this altogether?
Thanks in advance
class BigPanel
{
public JPanel bigPanel= new JPanel(new GridLayout(3,3));
public JButton[][] buttons = new JButton[3][3];
public BigPanel()
{
for (int i=0; i<3; i++)
{
for (int j=0; j<3; j++)
{
buttons[i][j] = new JButton();
buttons[i][j].setPreferredSize(new Dimension(75,75));
// Line of interest, adding the listener
buttons[i][j].addActionListener(new CustomActionListener(buttons[i][j]));
bigPanel.add(buttons[i][j]);
}
}
}
}
class CustomActionListener implements ActionListener
{
public int a;
public int b;
public JButton button;
CustomActionListener(JButton a)
{
button = a;
}
public void actionPerformed(ActionEvent e)
{
changeButton(a, b, CurrPlayer.CROSSES);
}
public void changeButton(int a, int b, CurrPlayer player)
{
if (player == CurrPlayer.NOUGHTS)
{
Icon icon = new ImageIcon("Nought.jpg");
button.setIcon(icon);
}
else
{
Icon icon = new ImageIcon("Cross.jpg");
button.setIcon(icon);
}
}
}
Change your CustomActionListener to following will solve this problem
class CustomActionListener implements ActionListener {
public int a;
public int b;
CustomActionListener()
{
}
public void actionPerformed(ActionEvent e)
{
JButton button = (JButton) e.getSource();
changeButton(a, b, CurrPlayer.CROSSES, button);
}
public void changeButton(int a, int b, CurrPlayer player, JButton button)
{
if (player == CurrPlayer.NOUGHTS)
{
Icon icon = new ImageIcon("Nought.jpg");
button.setIcon(icon);
}
else
{
Icon icon = new ImageIcon("Cross.jpg");
button.setIcon(icon);
}
}
}
You can get JButton reference using actionEvent.getSource() method.
And add action listener to buttons as below
buttons[i][j].addActionListener(new CustomActionListener());
Hope this'll help you.
I'd like to change value of my variable "name" when I select right button and click "ok" on my JRadio Frame.
For example when i select r1 and hit "ok" I'd like to have name=="Fast" in the entire package.
package Snake;
public class Radio extends JFrame {
private int delay = 100;
private String name;
JTextField t1;
JButton b;
JRadioButton r1, r2;
JLabel l;
public void selectSpeed() {
b = new JButton("Ok");
r1 = new JRadioButton("Fast");
r2 = new JRadioButton("Slow");
l = new JLabel("Speed: ");
ButtonGroup bg = new ButtonGroup();
bg.add(r1);
bg.add(r2);
add(b);
add(r1);
add(r2);
add(l);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if (r1.isSelected()) {
name = "Fast";
} else {
name = "Slow";
}
l.setText("Speed: " + name); // name=="Fast" when r1 is selected
} // name=="Slow" when r2 is selected
});
if (name == "Fast") { // and now name is empty...
delay = 50;
}
if (name == "Slow") {
delay = 500;
}
setLayout(new FlowLayout());
setSize(400, 400);
setVisible(true);
}
public int setSpeed() {
selectSpeed();
return delay;
}
}
If you want to change the delay on button click, You need to write the logic in the ActionListener itself because the code you have written to change the delay will run only once and that too at the start of the execution of your program and at that time, name will be empty.
Then when ever you click the button, It will only execute the ActionListener So delay will not be changed at any time. And other mistake you are making is that you are comparing Strings in wrong way. For more information take a look at it How do I compare Strings in Java?
To change delay dynamically on button click, you need to change it in the ActionListener.
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if (r1.isSelected()) {
name = "Fast";
delay = 50;
} else {
name = "Slow";
delay = 500;
}
l.setText("Speed: " + name); // name=="Fast" when r1 is selected
} // name=="Slow" when r2 is selected
});
You need to do it in your JRadioButton listener. For example, like here, at first you change the variable "name" and later in the current listener you check conditions, but you need remember that to compare the strings you need to use "equals":
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if (r1.isSelected()) {
name = "Fast";
} else {
name = "Slow";
}
l.setText("Speed: " + name); // name=="Fast" when r1 is selected
if (name.equals("Fast")) { // and now name is empty...
delay = 50;
}
if (name.equals("Slow")) {
delay = 500;
}
} // name=="Slow" when r2 is selected
});
Well I see my mistake now, Thank you.
But it still does not work the way I like. I'd like to change the "delay" value every time I select right button on JRadio and hit "ok" and with this changed value I'd like to go to the other class.
There is the code of a class where I need value of "delay":
package Snake;
public class Gameplay extends Paint implements KeyListener, ActionListener {
private Timer timer;
private int q = 0;
Radio radio = new Radio();
public Gameplay() {
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
timer = new Timer(radio.selectSpeed(), this); //here i need flexible "delay" value
timer.start();
}
I need a listener that will constantly check if a static boolean value has been changed so that I can repaint a component on a frame. Can someone please help me I really don't know much about listeners and haven't worked with them much? Help will be greatly appreciated.
edit(more clarity): I have two separate classes in which on class is the "main frame" the second class is an extension of JLabel and implements MouseListner for a "clickable photo". The "main frame" creates instances of the photo and when the photo is clicked the "main frame" is supposed to paint on the panel a description of the photo. This is "main frame"
MenuBar menuBar;
static AnnotationVisual a;
Picture pic;
Picture pic2;
GalleryScreen(int rows, int columns){
this.setBorder(BorderFactory.createEmptyBorder(500,500,0,0));
pic = new Picture("pic1", "Z:/My Documents/Downloads/Ball.jpg", new Coordinate(0,0));
pic2 = new Picture("pic2", "Z:/My Documents/Downloads/hoop.jpg" , new Coordinate(1,0));
this.add(pic);
this.add(pic2);
a = new AnnotationVisual();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
if(a.shouldAnnotate()){
FontMetrics size= g.getFontMetrics();
if(getWidth()>=(a.dispX()+size.stringWidth(a.annotationText()))){
g.setColor(Color.white);
g.fillRect(a.dispX()-3,a.dispY()-12,size.stringWidth(a.annotationText())+5,15);
g.setColor(Color.black);
g.drawRect(a.dispX()-3,a.dispY()-12,size.stringWidth(a.annotationText())+5,15);
g.drawString(a.annotationText(), a.dispX(), a.dispY());
}else{
String sub="";
int letters=0;
g.setColor(Color.white);
g.fillRect(a.dispX()-3,a.dispY()-12,getWidth(),15);
g.setColor(Color.black);
for(int i=0;i<a.annotationText().length();i++){
if(a.dispX()+letters+16<=getWidth()){
sub+=a.annotationText().substring(i,i+1);
letters=size.stringWidth(sub);
}else{
sub=sub+"...";
i=a.annotationText().length();
}
}
g.drawRect(a.dispX()-3,a.dispY()-12,size.stringWidth(sub)+3,15);
g.drawString(sub,a.dispX(),a.dispY());
}
}
}
public static AnnotationVisual getA()
{
return a;
}
This is "clickable photo"
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.*;
import javax.swing.*;
public class Picture extends JLabel implements MouseListener
{
String myAnnotation;
String filePath;
Coordinate imageCoord;
private boolean wasDoubleClick;
private Timer timer;
EditAnnotation newEdit;
AnnotationVisual newVisual;
public Picture(String annotation, String filePath, Coordinate coord)
{
super(new ImageIcon(filePath));
this.addMouseListener(this);
myAnnotation=annotation;
this.filePath = filePath;
imageCoord = coord;
newEdit = new EditAnnotation(annotation);
newVisual = new AnnotationVisual();
}
public Picture(String filePath)
{
super(new ImageIcon(filePath));
this.addMouseListener(this);
this.filePath = filePath;
newEdit = new EditAnnotation();
newVisual = new AnnotationVisual();
}
public String getAnnotation()
{
return myAnnotation;
}
public AnnotationVisual getAnnotationVisual()
{
return newVisual;
}
public void setAnnotation(String annotation)
{
myAnnotation=annotation;
}
public Coordinate getCoordinate()
{
return imageCoord;
}
public void setCoordinate(Coordinate coord)
{
imageCoord = coord;
}
public Dimension getSize()
{
return new Dimension(super.getIcon().getIconWidth(), super.getIcon().getIconHeight());
}
public void mouseClicked(MouseEvent e)
{
final int scrLocX = (int)e.getLocationOnScreen().getX();
final int scrLocY = (int)e.getLocationOnScreen().getY();
if (e.getClickCount() == 2)
{
wasDoubleClick = true;
}
else if(e.getClickCount() == 1)
{
Integer timerinterval = (Integer) Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval");
timer = new Timer(timerinterval.intValue(), new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
if (wasDoubleClick)
{
GalleryScreen.getA().deleteAnnotation();
myAnnotation = newEdit.getAnnotation();
newEdit.show(myAnnotation);
wasDoubleClick = false;
}
else
{
GalleryScreen.getA().deleteAnnotation();
GalleryScreen.getA().showAnnotation(scrLocX, scrLocY , myAnnotation);
}
}
});
timer.setRepeats(false);
timer.start();
}
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
}
AnnotationVisual is the thing that supposed to pop up when single clicked
You're probably better off making the boolean private, and only allowing it to be changed through a setter method. The setter method, when called, should then repaint the component.
The point of listeners is to invert the logic. You don't constantly check if a value is changed. You notify the listener when you change the value.
So, instead of Foo.bar = 5, you invoke Foo.setBar(5), where in addition to the assignment, you call barListener.valueChanged(value)
As a sidenote - avoid storing state in static variables.
You don't set a listener on a field in Java, you set it on a property. While properties (according to the JavaBeans spec) can be fields, they're usually done as pairs of methods (one getter, one setter; the latter being not needed for read-only fields) as that lets you hook extra logic in to be called when the property is accessed. Such as firing a listener callback to say that the value has changed. (You could use a thread to monitor for that sort of thing, but that's really nasty and error-prone. Wasteful too.)
One thing to be aware of though: you don't know what thread the value will have been modified from. Take care when invoking back into Swing…
I have a problem with the code I am currently trying to run - I am trying to make 3 buttons, put them on a GUI, and then have the first buttons colour be changed to orange, and the buttons next to that colour change to white and green. Every click thereafter will result in the colours moving one button to the right. My code thus far is as follows, it is skipping colours in places and is not behaving at all as I expected. Can anyone offer some help/guidance please ?
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class ButtonJava extends JButton implements ActionListener {
private int currentColor=-1;
private int clicks=0;
private static final Color[] COLORS = {
Color.ORANGE,
Color.WHITE,
Color.GREEN };
private static ButtonJava[] buttons;
public ButtonJava( ){
setBackground( Color.YELLOW );
setText( "Pick ME" );
this.addActionListener( this );
}
public static void main(String[] args) {
JFrame frame = new JFrame ("JFrame");
JPanel panel = new JPanel( );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE);
buttons = new ButtonJava[3];
for(int i = 0;i<buttons.length ; i++){
buttons[i] = new ButtonJava();
panel.add(buttons[i]);
}
frame.getContentPane( ).add( panel );
frame.setSize( 500, 500);
frame.setVisible( true );
}
private void updateButton() {
clicks++;
changeColors();
// setText( );
}
private void changeColors( ) {
for (int i=buttons.length-1;i>=0;i--){
buttons[i].currentColor = nextColor(currentColor);
buttons[i].setBackground(COLORS[buttons[i].currentColor]);
buttons[i].setText(("# of clicks = " + buttons[i].getClicks() ) );
}
}
private Integer getClicks() {
return clicks;
}
private int nextColor( int curCol ) {
final int colLen = COLORS.length;
curCol--;
curCol = (colLen + curCol % colLen) % colLen;
return curCol;
}
private void firstClick( ActionEvent event ) {
int curCol = 0;
for (int i=buttons.length-1;i>=0;i--){
if ( buttons[i] == event.getSource() ) {
buttons[i].currentColor = curCol;
curCol++;
currentColor++;
}
}}
#Override
public void actionPerformed( ActionEvent event ) {
if ( -1 == currentColor ) {
firstClick( event );
}
updateButton( );
}
}
Thank you very much for the help :)
You have a couple issues with the code you posted, but they generally boil down to being clear about what is a member of the class(static) and what is a member of the instance.
For starters, you buttons array only exists inside your main method and can't be accessed by changeColors(). Along those same lines, since changeColors() is an instance method, setBackground() needs to be called directly on the button in your array. as written you are setting the color for one button 3 times.
Additionally, the logic in changeColors() is not properly rotating the currentColor index. You need to both increase the counter and ensure is wraps for the length of the color array. If the arrays are the same size, you need to make sure there is an extra addition to make the colors cycle.
private static void changeColors( ) {
for (int i=0;i<buttons.length;i++){
buttons[i].setBackground(COLORS[currentColor]);
currentColor = nextColor(currentColor);
}
if (buttons.length == COLORS.length) {
currentColor = nextColor(currentColor);
}
}
private static int nextColor(int currentColor) {
return (currentColor+1)% COLORS.length;
}
Edit for new code:
I'm not sure why you re-wrote nextColor(), as what I posted worked. But in general, I feel like you are running into issues because your code is not well partitioned for the tasks you are trying to achieve. You have code related to the specific button instance and code related to controlling all the buttons mixing together.
With the following implementation, the issue of how many times a button was clicked is clearly self-contained in the button class. Then every button press also calls the one method in the owning panel. This method knows how many buttons there are and the color of the first button. And each subsequent button will contain the next color in the list, wrapping when necessary.
public class RotateButtons extends JPanel {
private static final Color[] COLORS = { Color.ORANGE, Color.WHITE, Color.GREEN };
private static final int BUTTON_COUNT = 3;
private JButton[] _buttons;
private int _currentColor = 0;
public static void main(String[] args)
{
JFrame frame = new JFrame("JFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new RotateButtons());
frame.setSize(500, 500);
frame.setVisible(true);
}
public RotateButtons()
{
_buttons = new JButton[BUTTON_COUNT];
for (int i = 0; i < _buttons.length; i++) {
_buttons[i] = new CountButton();
add(_buttons[i]);
}
}
private void rotateButtons()
{
for (JButton button : _buttons) {
button.setBackground(COLORS[_currentColor]);
_currentColor = nextColor(_currentColor);
}
if (_buttons.length == COLORS.length) {
_currentColor = nextColor(_currentColor);
}
}
private int nextColor(int currentColor)
{
return (currentColor + 1) % COLORS.length;
}
private class CountButton extends JButton {
private int _count = 0;
public CountButton()
{
setBackground(Color.YELLOW);
setText("Pick ME");
addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0)
{
_count++;
setText("# of clicks = " + _count);
rotateButtons();
}
});
}
}
}
2nd Edit:
Shows just the changes to shift _currentColor by the necessary amount on the first click.
public class RotateButtons extends JPanel {
...
private boolean _firstClick = true;
...
private void rotateButtons(CountButton source)
{
if (_firstClick) {
_firstClick = false;
boolean foundSource = false;
for (int i = 0; i < _buttons.length; i++) {
if (foundSource) {
_currentColor = nextColor(_currentColor);
} else {
foundSource = _buttons[i] == source;
}
}
}
...
}
private class CountButton extends JButton {
...
public CountButton()
{
...
addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0)
{
...
rotateButtons(CountButton.this);
}
});
}
}
One thing that I noticed is you are using currentColor to assign the color, but the currentColor is initialized to 0 and the only manipulation is currentColor %= 2 which does not change it.
If I'm understanding what you are wanting to achieve, I'm thinking to change currentColor %= 2 to ++currentColor, and setBackground(COLORS[currentColor]); to buttons[i].setBackground(COLORS[(i + currentColor) % 3]);. That way, your colours should cycle around your buttons each time one is clicked.
EDIT: it's probably also worth calling changeColors from within main to initialise your button colours. And, as #unholysampler notes, your array buttons is local to main and should (for example) be refactored as a static member variable, and have changeColors become a static method.