I have created a board game in Java that is not different in principle from checkers. It works fine in the console but now I'm trying to create graphics. I have a Piece class, a Tile class that checks if it is empty or occupied by a white or a black checkers piece, a Grid class that keeps track of the tiles in a matrix, and a Game class.
Currently, the game can be played in the Grid class; when we run the Grid class, the user specifies the size of the board in the console and then plays the game by giving the x and y coordinates of the tile that the user wishes to select. What I would like to change is to run the game in the Game class, which is an extension of JPanel and implements MouseListener (code given below). The game board will be a fixed size (I'll begin with 5x5) and I have drawn a picture of a grid which should be in the background of the game. There will be an instance variable (Grid g = new Grid(5,5)). I have also drawn pictures of the different "checkers" pieces which will be used, they should be distributed in the foreground on specific tiles. What I want to happen is that when the user clicks a tile, the checkers pieces move. Ideally, I would do this so that the program sees the coordinates of the place that the mouse clicks (say that the JPanel is 500x500 pixels and the user clicks the pixel with the coordinate (0,500), then we check if (0,500) belongs to some tile, if it doesn't then nothing happens, if it does belong to a tile on the Grid g then g.play(something,something)).
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.FlowLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
public class Game extends JPanel implements MouseListener {
private Grid g = new Grid(5,5);
public Game() {
JFrame frame = new JFrame("Boardgame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.setLayout(new FlowLayout());
frame.setPreferredSize(new Dimension(500,500));
frame.pack();
frame.setVisible(true);
frame.addMouseListener(this);
}
public void mouseClicked(MouseEvent e) {
//here we check if the user clicked on a tile,
if that happens then we get the x and y-coordinates of the tile and then g.play(x,y)
}
public void mouseEntered(MouseEvent e) {
// we are not really interested in this method or the following
mouse methods but they are necessary for the mouselistener
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public static void main(String args[]) {
new Game();
}
}
So in essence, what I would like to do is:
i) Have a background picture (this is easy right?) [update: I have done this by means of using a JLabel, if there is any better way to do it please tell me].
ii) Fix the MouseListener so that the pieces move when a tile is clicked. The only input I require is which tile is to be moved, we don't need to know which piece is supposed to go there.
Any help would be really appreciated and please ask if I can clarify something. This is not for school or anything, just a private project.
You could use Shape and attach listeners to catch user actions on the shapes.
To enable the user to interact with the graphics you display, you need to be able to determine when the user clicks on one of them. The hit method of the Graphics2D class provides a way to easily determine whether a mouse click occurred over a particular Shape object. Alternatively you can get the location of the mouse click and call contains on the Shape to determine whether the click was within the bounds of the Shape.
read on http://docs.oracle.com/javase/tutorial/2d/advanced/user.html
Related
What I want to do:
I want to write a small application that can display an image. The user has to be able to zoom in and out of the image, move it around, and mark points on the image. Further down the line I want to analyze the points clicked, but I'm not there yet.
What I have so far:
In order to track down my problem I wrote a MVCE:
GUI class for handling the JFrame (and other UI elements later):
import javax.swing.*;
import java.net.MalformedURLException;
import java.net.URL;
public class MCVE_GUI {
public static void main(String[] args) throws MalformedURLException {
MCVE_ZoomPane zp = new MCVE_ZoomPane(new URL("https://fiji.sc/site/logo.png"));
JFrame f = new JFrame("PictureMeasurement");
f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
f.setContentPane(zp);
f.pack();
f.setLocationRelativeTo(null);
f.revalidate();
f.repaint();
f.setVisible(true);
}
}
ZoomPanel for handling the image and zooming:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.net.URL;
class MCVE_ZoomPane extends JPanel implements MouseMotionListener {
MCVE_ZoomPane(URL url){
JLabel image = new JLabel();
JScrollPane jsp = new JScrollPane(image);
//image.setIcon(new ImageIcon(url)); // picture, no input
//jsp.setPreferredSize(new Dimension(300,300)); //picture, no input
jsp.setPreferredSize(image.getPreferredSize()); //depends on position of image.setIcon
image.setIcon(new ImageIcon(url)); //no picture, input
this.add(jsp);
this.setPreferredSize(image.getPreferredSize());
this.addMouseMotionListener(this);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
}
public void mouseDragged(MouseEvent e) {
System.out.format("Dragged X:%d Y:%d\n",e.getX(), e.getY());
}
public void mouseMoved(MouseEvent e) {}
}
The problem:
Depending on where I put the image.setIcon(new ImageIcon(url)) I get either the image displayed or can listen to mouse clicks, but not both together. If I set the JScrollPane to a fixed preferred size without calling image.getPreferredSize() I always get a picture but no input.
Apparently I'm stupid. The JScrollPane/JLabel covered the JPanel which was the only component which had a MouseMotionListener. The solution is to add the single line of image.addMouseMotionListener(this);.
I thought about and tried different soltuions to this for at least three hours now. It's a hobby project, so no time constaints, but man do I feel stupid now.
i have an assignment to make a simple drawing application using swing and mouse listener. the application has to have three classes, one that contains Main, one that contains the frame, and the last one that makes the drawing. The teacher gave us a source code we're supposed to use to complete the assignment and it looks like this:
import java.awt.*;
import java.awt.event.*;
import javax.swing.JFrame;
public class Drawsome extends JFrame implements MouseMotionListener {
public Drawsome(){
setSize(300,400);
setForeground(Color.black);
show();;
addMouseMotionListener(this);
}
public void mouseDragged(MouseEvent evt) {
start = end;
end = new Point(evt.getX(),evt.getY());
repaint();
}
public void mouseMoved(MouseEvent evt) {
end = null;
}
public void paint(Graphics g) {
if (start!=null && end!=null)
g.drawLine(start.x, start.y, end.x, end.y);
}
public void update(Graphics g) {
paint(g);
}
Point start=null;
Point end=null;
}
now this work perfectly, but since we have to make the frame in another class i tried to do this:
import java.awt.Color;
import javax.swing.JFrame;
public class MainWindow extends JFrame {
public MainWindow() {
setSize(300,400);
setForeground(Color.black);
show();;
}
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.JFrame;
public class Drawsome extends JFrame implements MouseMotionListener {
public Drawsome(){
MainWindow mainwindow = new MainWindow();
addMouseMotionListener(this);
} (rest is the same as the previous code)
i'll get a frame, but the rest doesn't work, i dont' understand what i'm doing wrong, and would greatly appreciate a push in the right direction
Your teacher's source code is terrible since you should never be drawing within the paint method or within a JFrame, plus his/her paint override doesn't call the super's method, breaking the painting chain. They don't appear to know what they're doing.
Having said that, your main driver should not extend JFrame nor should you try to create a JFrame of it or even an instance of it. Instead, in this class's main method, create an instance of the terrible drawing class.
Note that I don't understand this requirement:
and the last one that makes the drawing.
Please post the exact requirements.
If this were my application, I'd
Do my drawing within a JPanel, and not a JFrame
Do it within the JPanel's paintComponent method, not the paint method.
Be sure to call the super's paintComponent method within my JPanel's paintComponent method override.
Then place my JPanel within a JFrame to display it.
I would not have my GUI class implement the MouseListener but rather would use a nested inner class for this.
I am trying to write a test java program in order to learn how mouse listeners work in java. Basically, I was trying to write a graphics program and that if I click on the canvas, the program generates a label "hello" at the place where I clicked my mouse. But nothing happens after I click my mouse. Here is my code. Can anybody help me with it?
import acm.program.*;
import acm.graphics.*;
import java.awt.event.*;
public class test extends GraphicsProgram{
public void run(){
addMouseListeners();
}
public void MouseClicked(MouseEvent e){
label=new GLabel("Hello",e.getX(),e.getY());
add(label);
}
private GLabel label=null;
}
I have a boolean that changes when the mouse is within the set co-ordinates inside the JFrame and then I have an if in my run method so that I can change the cursor but I cannot find anywhere of how to change the cursor without using a list or object. How would I change the cursor without using an object other than JFrame?
I'm not really sure why you would need to 'not' use other objects. If you were willing to use something like a JPanel, your code could become very simple. For example, you could add a JPanel to the area in question, and then set a cursor to recognize each time the cursor entered or left that swing component. For example;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ExampleClass{
public static void main(String args[]){
JFrame exampleFrame = new JFrame("Test");
exampleFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
exampleFrame.setLayout(new FlowLayout());
exampleFrame.setSize(300, 300);
JPanel redPanel = new JPanel();
redPanel.setBackground(Color.RED);
redPanel.setPreferredSize(new Dimension(150, 300));
redPanel.setCursor(new Cursor(Cursor.HAND_CURSOR)); // This one line changes the cursor.
exampleFrame.add(redPanel);
exampleFrame.setVisible(true);
}
}
Although it is entirely possible to do what your asking, it sounds as though it would be much more verbose/less readable than is necessary to see results. The JPanel wouldn't even need to be a different color, it could be completely invisible to the user (redPanel.setOpaque(false); would do the trick).
Running the example code above shows a JFrame with a centralized red square (the JPanel redPanel). Moving your mouse over the red square changes the cursor.
If for some reason the above method is impractical or your program design is very much set in it's ways, you could use absolute co-ordinates relative to your JFrame. Create a MouseMotionListener for your JFrame, like so;
class CursorMouseMotionListener implements MouseMotionListener{
JFrame yourFrame;
CursorMouseMotionListener(JFrame yourFrame){
this.yourFrame = yourFrame;
}
#Override
public void mouseDragged(MouseEvent e) {
// Do nothing.
}
/// The important bit below
#Override
public void mouseMoved(MouseEvent e) {
if(e.getPoint().x >= 50 && e.getPoint().x <=150 && e.getPoint().y >= 50 && e.getPoint().y <=150){
yourFrame.setCursor(new Cursor(Cursor.HAND_CURSOR));
}
else{
yourFrame.setCursor(new Cursor(Cursor.MOVE_CURSOR));
}
}
}
Then add this to your JFrame like so;
frame.addMouseMotionListener(new CursorMouseMotionListener(frame));
You will have to change the coordinates, of course, to match the position of your buttons but then your good. I still wouldn't advise this method if you can avoid it. Better to use the swing components at your disposal. But if you absolutely need to with your current program then this should work.
I'm doing a project where i need some custom swing components. So far I have made a new button with a series of images (the Java Metal look doesn't fit with my UI at all). Ive implemented MouseListener on this new component and this is where my problem arises. My widget changes image on hover, click etc except my MouseListener picks up mouse entry into a the entire GridLayout container instead of into the image. So I have an image of about 200*100 and the surrounding container is about 400*200 and the mouseEntered method is fired when it enters that GridLayout section (even blank space parts of it) instead of over the image. How can I make it so that it is only fired when I hover over the image? Ive tried setting size and bounds and other attributes to no avail.
EDIT: Here's a demonstration of my issue. As you can see (sort of, colors are very similar) the bottom right button is highlighted just by entering its section of the GridlLayout. I only want it highlighted when I'm over the image actual, not the GridLayout section.
I Won't add the MouseListener methods because they just involve switching the displayed image.
public customWidget()
{
this.setLayout(new FlowLayout());
try {
imageDef=ImageIO.read(new File("/home/x101/Desktop/buttonDef.png"));
imageClick=ImageIO.read(new File("/home/x101/Desktop/buttonClick.png"));
imageHover=ImageIO.read(new File("/home/x101/Desktop/buttonHover.png"));
current=imageDef;
} catch (IOException e)
{
e.printStackTrace();
}
this.addMouseListener(this);
}
protected void paintComponent(Graphics g)
{
super.paintComponents(g);
g.drawImage(current, 0, 0, current.getWidth(), current.getHeight(), null);
}
EDIT: added code section
As an alternative, consider the The Button API, which includes the method setRolloverIcon() "to make the button display the specified icon when the cursor passes over it."
Addendum: For example,
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ButtonIconTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
private static void createAndShowGui() {
String base = "http://download.oracle.com/"
+ "javase/tutorial/uiswing/examples/components/"
+ "RadioButtonDemoProject/src/components/images/";
ImageIcon dog = null;
ImageIcon pig = null;
try {
dog = new ImageIcon(new URL(base + "Dog.gif"));
pig = new ImageIcon(new URL(base + "Pig.gif"));
} catch (MalformedURLException ex) {
ex.printStackTrace(System.err);
return;
}
JFrame frame = new JFrame("Rollover Test");
JPanel panel = new JPanel();
panel.add(new JLabel(dog));
panel.add(new JLabel(pig));
JButton button = new JButton(dog);
button.setRolloverIcon(pig);
panel.add(button);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
I assume your image contains ONLY 4 'customWidget' objects (in a 2x2 grid).
Your code is working as I would expect. Your MouseListener methods are responding to MouseEvents for 'customWidget' (not the image drawn in 'customWidget'), which is sized to take up 1/4 of the image, so they will respond when it enters the enlarged area. The error is actually in your Test program, because you are allowing the custom button widget to be larger than the image.
If you want a Test program that provides an image similar to yours, you should create a larger grid (say 4x4), and then only place your buttons in every other grid node. Place an empty component into the gaps.
Although I won't answer to your particular question, I hope this helps:
If the components just look wrong maybe you should reuse Swing components and just write a custom Look&Feel or theme.
It would certainly help ensuring the look of the application is consistent and at least you are using the right tool for the task you want to accomplish.
As a sidenote, be aware that Java comes with multiple Look&feels, including Look&Feels made to mimic the native OS theme.
See: http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html