Hello all,

I'm making a simple game in minesweeper, and I have a Mine class with a JButton and some other information, namely an x and y variable for its location. In a class called Board, a constructor creates a multi-dimensional array of Mine objects. I tried adding mouselistener to the constructor of buttons but it did not work, only registering the last object of the array. When I add the MouseListener to the Board constructor, I cannot refer to the x and y variables inside the action performed in MouseListener, as shown below:

int yCounter = 0;
		int xCounter = 0;
		
	
		for(xCounter = 0;xCounter < 10;xCounter++){
			for(yCounter = 0;yCounter < 10; yCounter++){
				array[xCounter][yCounter] = new Mine(xCounter, yCounter);
				frame.add(array[xCounter][yCounter].getButton());
				frame.add(array[xCounter][yCounter].getNumber());
				
				array[xCounter][yCounter].getButton().addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e){
				if(e.getButton() == 3)
					System.out.println("right click at" + array[xCounter][yCounter].getX() + array[xCounter][yCounter].getY());
			}
		});

			}
			yCounter = 0;
			}

However I cannot refer to the xCounter and yCounter variables, but I need to get those variables in order for the actual game functions (not used at this time)

Any ideas on how i can add a listener and still get those x and y variables?

Any help is appreciated,
Phil

Recommended Answers

All 15 Replies

Define your mouse listener to accept a Mine reference in it's constructor and give it the mine object reference when you add the listener to the button

class MineListener extends MouseAdapter {
    Mine mine;

    public MineListener(Mine mine) {
        this.mine = mine;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        // do whatever you want with mine
    }
}

Or just implement mouse listener on your Mine class and pass it the directly to the button.

So would this be inside a separate class? Or would this be inside the board class?

EDIT: So I added this to a new minelistener class, and then made an array of listeners for each mine in the board class like this:

MineListener class

class MineListener extends MouseAdapter {
	Mine mine;
	
	public MineListener(Mine mine) {
		this.mine = mine;
		
	
	}
	
	@Override
	public void mouseClicked(MouseEvent e){
		if (e.getButton() == 3)
		{
			System.out.println("Right clicked at " + mine.getX() + mine.getY());
		}
		if (e.getButton() == 1)
		{
			System.out.println("YES");
		}
	}
	
	
}

And then I used it in the board class like this:

for(xCounter = 0;xCounter < 10;xCounter++){
			for(yCounter = 0;yCounter < 10; yCounter++){
				array[xCounter][yCounter] = new Mine(xCounter, yCounter);
				frame.add(array[xCounter][yCounter].getButton());
				frame.add(array[xCounter][yCounter].getNumber());
				
				listener[xCounter][yCounter] = new MineListener(array[xCounter][yCounter]);
			}
			yCounter = 0;
			
		}

I don't know if I'm doing what you say? Thank you though for the help.

I'm not sure what listener[][] array is for. Are you not just adding the listener to the JButton? You don't really need an array of listener objects, but then I don't know the full context behind that code fragment.

Oh, I'm sorry if I didn't make myself clear, what I mean to say is that I need a listener for each button, so if I have a grid of 100 buttons, I need a way to tell when each one was clicked.

Listeners are added to the buttons though. There is no reason to keep a separate array of them.

Ok, I am doing what I believe you are saying, basically I'm adding the listener now each time a button is constructed, but yet nothing is being output when I click

int yCounter = 0;
		int xCounter = 0;
		
	
		for(xCounter = 0;xCounter < 10;xCounter++){
			for(yCounter = 0;yCounter < 10; yCounter++){
				array[xCounter][yCounter] = new Mine(xCounter, yCounter);
				frame.add(array[xCounter][yCounter].getButton());
				frame.add(array[xCounter][yCounter].getNumber());
				
				listener = new MineListener(array[xCounter][yCounter]);
			}
			yCounter = 0;
			
		}

My mouseClicked code is something simple for testing purposes, just a println statement but yet nothing is happening.

You have to add the listener directly to the JButton with addMouseListener(), so wherever you are creating the button for the mine object, you need to create and add the listener to it.

Ok so now I added the listener to the button, but now I have run into a problem, I set the mouseClicked function to display which coordinates the button was clicked at, but now I am only getting the coordinates for the last button created, at (9,9). How should I change it to fix this?

listener = new MineListener(array[xCounter][yCounter]);
				array[xCounter][yCounter].getButton().addMouseListener(listener);

The above is how I added the listener to the button.

Nothing really stands out as a problem with that, but your code would be a little cleaner to read if you altered it just a bit

for(xCounter = 0;xCounter < 10;xCounter++){
			for(yCounter = 0;yCounter < 10; yCounter++){
				Mine mine = new Mine(xCounter, yCounter);
                                mine.getButton().addMouseListener(new MineListener(mine));
				frame.add(mine.getButton());
				frame.add(mine.getNumber());
				
				array[xCounter][yCounter] = mine;
			}
			yCounter = 0;
		}

Ok, thank you for that bit, and I did change my code to that, but yet it still will only print the coordinates of the last jbutton, even when I press other buttons.

You haven't really posted enough code to determine the cause. I don't see any immediate problem with your last bit, but there are other things that could be occurring.

Are you sure that you aren't accidentally adding the same JButton to multiple Mine instances? That would produce the behavior you're describing.

Perhaps your Mine class should just implement MouseListener and respond to the click itself?

Well, I fixed that problem by sending the xCounter and yCounter directly to the MineListener class, but now I have run into a different but related problem. In my code, I have a function called leftClick for when a button is left clicked. It displays in the prompt the coordinates where it was clicked, but when I go to set the JButton to invisible, and set a JTextArea as visible, it only does this in the (9,9) spot, no matter where I click. Here's the full MineListener class below:

package gameAlgorithm;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JButton;
import javax.swing.JFrame;

class MineListener extends MouseAdapter {
	Mine mine;
	int x;
	int y;
	Mine[][]array;
	JFrame frame;
	
	public MineListener(Mine mine, int x, int y, Mine[][] array, JFrame frame) {
		this.mine = mine;
		this.x = x;
		this.y = y;
		this.array = array;
		this.frame = frame;
	}
	
	@Override
	public void mouseClicked(MouseEvent e){
		if (e.getButton() == 3)
		{
			System.out.println("Right clicked at " + x + " and at " + y);
		}
		if (e.getButton() == 1)
		{
			System.out.println("YES at " + x + " and at " + y);
			leftClick();
		}	
	}
	
	public void leftClick(){
		if(mine.getSet() == true){
			System.out.println("You have failed");	
		}
		else if(mine.getSet() == false){
			if(mine.getStatus() == 0){
				mineClick();
			}
			else if(mine.getStatus() == 1){		
			}
		}
	}
	
	//changes a button to a text area
	public void mineClick(){
		int around;
		around = mineAround(mine.getX(), mine.getY());
		if(around == 0){
			//for testing purposes only, no mines are set yet, so all the buttons will have no mines around
			//when this works, it will be a recursion formula
			mine.getButton().setVisible(false);
			mine.getNumber().setText("WOO");
			mine.setStatus(2);
			mine.getNumber().setVisible(true);	
		}
		else{
			String value = Integer.toString(around);
			mine.getButton().setVisible(false);
			mine.getNumber().setText(value);
			mine.getNumber().setVisible(true);
			mine.setStatus(2);
			frame.repaint();
		}
	}
	
	//Check how many mines are around one mine object
	public int mineAround(int x, int y){
		int counter = 0;
		
		if((y-1)>= 0){   //Button above
			if(array[x][y-1].getSet() == true)
				counter++;
		}
		if((x+1)<10 && (y-1) >= 0){ //Button top right
			if(array[x+1][y-1].getSet() == true)
				counter++;
		}
		if((x+1)<10){ //Button right
			if(array[x+1][y].getSet() == true)
				counter++;
		}
		if((x+1)<10 && (y+1)< 10){ //Button lower right
			if(array[x+1][y+1].getSet() == true)
				counter++;
		}
		if((y+1) < 10){ //Button bottom
			if(array[x][y+1].getSet() == true)
				counter++;
		}
		if((x-1) >= 0 && (y+1) <10){  //Button bottom left
			if(array[x-1][y+1].getSet() == true)
				counter++;
		}
		if((x-1) >= 0) { //Button left
			if(array[x-1][y].getSet() == true)
				counter++;
		}
		if((x-1) >= 0 && (y-1) >=0){ //Button top left
			if(array[x-1][y-1].getSet() == true)
				counter++;
		}
		return counter;
	}
}

Thanks for the suggestions

As I already posted above:
Are you sure that you aren't accidentally adding the same JButton to multiple Mine instances? That would produce the behavior you're describing.

That applies to your current question as well. You should not have to send the x and y coordinates separately in the constructor. The fact that all actions seem to only be affecting that last button really does sound like you may be re-assigning the same button reference to multiple mines.

If you can post the code that creates the JButtons for the mines, that might help.

Alright, here's the constructor for the mine class

Mine(int xVariable, int yVariable){
		x = xVariable;
		y = yVariable;
		set = false;
		status = 0; //0, not clicked, 1 flagged, 2 displaying number
		button = new JButton();
		button.setBounds(xVariable * 20, yVariable * 20, 20, 20);
		button.setVisible(true);
	
		number = new JTextArea();
		number.setBounds(xVariable * 20, yVariable * 20, 20, 20);
		number.setText("0");
		number.setVisible(false);
	}

And I'm not sure what you mean when you say to make sure that I'm not accidentally adding the same JButton to multiple Mine objects.

Thank you for bearing with me haha, I'm still learning a lot about java.

EDIT: So I took your advice and took out the x and y in the constructor, and only relied on the mine.getX() and mine.getY() functions to access these values, but now I'm getting the problem again where it says that the button is only clicked at 9,9

Well, that looks just fine to me. Nothing really stands out that would cause the button problem. When I said sharing the button, I meant that perhaps you were unintentionally reassigning a single button reference to multiple mine objects. Or perhaps a single listener instance being shared across the buttons.

The behavior you're getting is what one would expect if you were repeatedly updating a single reference with new values in your loop. When it's done, it reflects whatever you set in the last iteration. That is why I wanted to see more of the code involved with the creation of the buttons and listeners.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.