//I am trying to learn how to draw objects in java. I'm getting better at it, but once I get an image on the screen I am having trouble manipulating it. The numbers I put in don't make sense to how the shapes are turning out. At least to me they don't. In algebra if you increase a number on the x axis it goes to the right and if you increase a number on the y axis it goes up. Thats not whats happening here. Can anyone explain to me how this works? I'm still new to java, so the more explanation and detail the better. I'm trying to take a couple of hours out a day over my summer to learn java and sometimes it gets a little frustrating. Any help is greatly appreciated.
Here the Co-ordinates start from the TOP LEFT SIDE of the screen, as as you increase value of X, you will move towards RIGHT SIDE, though as you increase the value of Y, you will move DOWNWARDS. Here is a small example Program for you to understand this a bit better, simply click on it anywhere.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DrawingExample
{
private int x;
private int y;
private String text;
private DrawingBase canvas;
private void displayGUI()
{
JFrame frame = new JFrame("Drawing Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
canvas = new DrawingBase();
canvas.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent me)
{
text = "X : " + me.getX() + " Y : " + me.getY();
x = me.getX();
y = me.getY();
canvas.setValues(text, x, y);
}
});
frame.setContentPane(canvas);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new DrawingExample().displayGUI();
}
});
}
}
class DrawingBase extends JPanel
{
private String clickedAt = "";
private int x = 0;
private int y = 0;
public void setValues(String text, int x, int y)
{
clickedAt = text;
this.x = x;
this.y = y;
repaint();
}
public Dimension getPreferredSize()
{
return (new Dimension(500, 400));
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawString(clickedAt, x, y);
}
}
Related
I'm trying to draw a rectangle to the screen based on mouse listeners. I'm having a few problems:
To start with the shape, it has an offset by a few pixels relative to the
release location of the mouse.
Secondly I can get only the shape to display if I set the screen to
visible each time after adding a JPanel component.
Could anyone tell me what I'm doing wrong or point me in the direction of some documentation? (I’ve read the Oracle documentation but I didn’t really understand it)
I know this isnt the greatest code but i just want to get it working for the moment.
This is my first time asking a question on stackoverflow so if I'm doing something wrong pls tell me.
This is my code
public class Frame {
private JFrame frame;
public Frame() {
this.frame = frameSetup("Graph");
this.frame.addMouseListener(new Mouse());
}
private JFrame frameSetup(String heading) {
JFrame f = new JFrame(heading);
f.setBackground(Color.WHITE);
f.setSize(800, 800);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.setVisible(true);
return f;
}
public void draw(int xIn, int yIn, int widthIn, int heightIn) {
int x = xIn;
int y = yIn;
int width = widthIn;
int height = heightIn;
this.frame.add(new Panel(x, y, width, height));
this.frame.pack();
//this.frame.setVisible(true);
}
private class Mouse implements MouseListener {
int x = 0;
int y = 0;
int height = 0;
int width = 0;
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == 1) {
x = e.getX();
y = e.getY();
System.out.println(x + ": " + y);
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == 1) {
width = (e.getX() - x);
height = (e.getY() - y);
System.out.println(width + ": " + height);
draw(x - 7, y -30, width, height);
}
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
}
and
public class Panel extends JPanel {
private int x;
private int y;
private int width;
private int height;
public Panel(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(x, y, width, height);
}
}
Welcome to the Site!
The offset is something I've had plenty of trouble with in the past aswell. It's caused by adding the MouseListener to the JFrame rather than it's contentPane. Basically, the JFrame is the thin border and the bar with the name, the icons and the buttons at the top. The content pane sits inside that, here it's the white surface. When you add the MouseListener to the JFrame, the coords are relative to this outline (the top left corner of the frame), rather than the white panel. This is fixed as follows:
// in the Frame constructor
this.frame.getContentPane().addMouseListener(new Mouse());
// in Mouse.mouseReleased(), remove the offset you added.
draw(x, y, width, height);
The Panel problem is a different story. I don't know how the code is doing what it does or how it works at all in the first place, but I can still give you some general tips.
Don't pack() the frame in the drawing method. It's used to calculate the window size while respecting the layout and the content of it. For me, it made the window as small as the OS allowed it to be.
Don't use JPanels like that! You'll run into weird problems like this, since they're made to be used in GUIs. For this reason, they're also way to heavy-weight to be used as a graphic. #user16320675 commented a tutorial on custom painting, I suggest you check that out.
Lastly, I suggest reworking the rectangle drawing. Right now, it only works when dragging from the top left to the bottom right.
EDIT: First question solved, see Roberto Attias 's answer and maybe read the comment . Still there's the second issue.
I have to do a small 2D game in Java and I want to stick to AWT as much as possible, ( so no SWING nor Java2D unless absolutely necessary).
I have a window and I can draw an image on it but there's two issues.
First I can't draw more than one Image. In fact with some of my test when in debug I can see that my program will draw my two images only to delete them and re-draw the first image.
Second, that first image which is re-drawn is not at the coordinate it should ( its slightly on the left and on below )
For now I have something like that:
public class AwtManager {
private Frame frame;
private Panel panel;
public AwtManager(){
frame = new Frame("a");
frame.setSize(800,600);
frame.setLocation(100, 100);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowEnvent){
System.exit(0);
}
});
panel = new Panel();
// here I tried to setBounds to solve my second issue, it didn't work
//panel.setBounds(0, 0, 800, 600);
frame.add(panel);
frame.setVisible(true);
}
This part open my windows and works quite nicely but my second issue seems to be caused by the borders of my frame / panel so there might be some changement to do here.
public void showMytwoImagesFFS(ArrayList<ImageComponent> images){
for (int i = 0; i < images.size(); i++) {
panel.add(images.get(i);
//And this is where I'm lost
}
// or here maybe
frame.setVisible(true);
}
}
In this second part I tried every combination of Component.paint(g), Component.update(g), Component.repaint(), Component.setVisible(true) I could think of.
My object ImageComponent is simply this:
public class ImageComponent extends Component {
private static final long serialVersionUID = 1L;
private BufferedImage img;
private int x;
private int y;
public ImageComponent(String path,int x, int y){
try{
img = ImageIO.read(new File(path));
this.x = x;
this.y = y;
}
catch (IOException e){
e.printStackTrace();
}
}
This function getPreferredSize() disturbs me like hell, it should return the preferred size of the ImageComponent but apparently I have to put the size of my frame/ panel otherwise it won't work.
public Dimension getPreferredSize(){
return new Dimension(800,600);
}
And finally my paint, update, repaint:
public void paint(Graphics g){
g.drawImage(img, x, y,null);
}
public void update(Graphics g){
super.update(g);
}
public void repaint(){
this.getGraphics().drawImage(img, x, y, null);
}
}
I highly doubt that those three look like what they should but I find the documents on the subject very hard to understand.
So pleas, could you help me with those issues, and by the way if you know how Component.setVisible(boolean) works i would like to know because in debug mod, this function made me loose some hair.
EDIT:
Here's a screenshot of my window knowing that I asked for two red square (there are Images not Rectangle), one a 0,0, the other at 200, 200.
EDIT 2:
Here's a fully runnable code (i think):
import java.awt.*;
import java.util.*;
import java.awt.event.*;
public class AwtManager {
private Frame frame;
private Panel panel;
public static void main(String args[]) {
new AwtManager();
ArrayList<ImageComponent> images = new ArrayList<>();
images.add(new ImageComponent("myimage.jpg", 0, 0));
images.add(new ImageComponent("myimage.jpg", 200, 200));
showMytwoImagesFFS(images);
}
public AwtManager(){
frame = new Frame("a");
frame.setSize(800,600);
frame.setLocation(100, 100);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowEnvent){
System.exit(0);
}
});
panel = new Panel();
frame.add(panel);
frame.setVisible(true);
}
public void showMytwoImagesFFS(ArrayList<ImageComponent> images){
for (int i = 0; i < images.size(); i++) {
panel.add(images.get(i));
}
frame.setVisible(true);
}
}
import java.io.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
public class ImageComponent extends Component {
private static final long serialVersionUID = 1L;
private BufferedImage img;
private int x;
private int y;
public ImageComponent(String path,int x, int y){
try{
img = ImageIO.read(new File(path));
this.x = x;
this.y = y;
}
catch (IOException e){
e.printStackTrace();
}
}
public Dimension getPreferredSize(){
return new Dimension(800,600);
}
public void paint(Graphics g){
g.drawImage(img, x, y,null);
}
public void update(Graphics g){
super.update(g);
}
public void repaint(){
this.getGraphics().drawImage(img, x, y, null);
}
}
Just a quick thought, but do you know the visibility of each of your images in that ArrayList? I.e.
public void showMytwoImagesFFS(ArrayList<ImageComponent> images){
for (int i = 0; i < images.size(); i++) {
images.get(i).setVisible(true);//is the visibility actually true?
panel.add(images.get(i);
}
// or here maybe
frame.setVisible(true);
}
Also, do you have screenshots of how your program looks at the moment? With gui problems, I find they're easier to solve if I can see what's going on.
This is your code, slightly modified to run. In the future, please post fully runnable examples.
import java.awt.*;
import java.util.*;
import java.awt.event.*;
public class AwtManager {
private Frame frame;
private Panel panel;
public static void main(String args[]) {
new AwtManager();
}
public AwtManager(){
frame = new Frame("a");
frame.setSize(800,600);
frame.setLocation(100, 100);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowEnvent){
System.exit(0);
}
});
panel = new Panel();
// here I tried to setBounds to solve my second issue, it didn't work
//panel.setBounds(0, 0, 800, 600);
frame.add(panel);
ArrayList<ImageComponent> images = new ArrayList<>();
images.add(new ImageComponent("myimage.jpg", 0, 0));
showMytwoImagesFFS(images);
frame.setVisible(true);
}
public void showMytwoImagesFFS(ArrayList<ImageComponent> images){
for (int i = 0; i < images.size(); i++) {
panel.add(images.get(i));
}
}
}
import java.io.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
public class ImageComponent extends Component {
private static final long serialVersionUID = 1L;
private BufferedImage img;
private int x;
private int y;
public ImageComponent(String path,int x, int y){
try{
img = ImageIO.read(new File(path));
this.x = x;
this.y = y;
}
catch (IOException e){
e.printStackTrace();
}
}
public Dimension getPreferredSize(){
return new Dimension(800,600);
}
public void paint(Graphics g){
g.drawImage(img, x, y,null);
}
public void update(Graphics g){
super.update(g);
}
public void repaint(){
this.getGraphics().drawImage(img, x, y, null);
}
}
It seems that there is a bug with Ubuntu (maybe only unity). The decoration of the JFrame is taken into account for getLocation() and getSize(), but not for setLocation() and setSize(). This leads to weird behaviour. For instance, if you use pack() after the frame is displayed and the dimensions changed, the frame will go down 20 pixels...
To illustrate a concrete case when it becomes really annoying, I made a SSCCE. It's a JFrame with a basic JPanel. If you drag the panel, the JFrame is supposed to move along.
Under Windows, it works as expected. Under Ubuntu, if I do setUndecorated(true) it will also work fine, but if I let the decoration, the JFrame turn crazy !
public class Test {
private static JFrame mainFrame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
mainFrame = new JFrame("test");
mainFrame.setSize(300,20);
mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
mainFrame.setVisible(true);
Container pane = mainFrame.getContentPane();
pane.addMouseMotionListener(new MouseMotionListener() {
private int posX = 0, posY = 0;
#Override
public void mouseDragged(MouseEvent e) {
int x = e.getX() - posX + mainFrame.getX();
int y = e.getY() - posY + mainFrame.getY();
mainFrame.setLocation(x, y);
}
#Override
public void mouseMoved(MouseEvent e) {
posX = e.getX();
posY = e.getY();
}
});
}
});
}
}
I don't know how I can fix that. How can I get the size of the windows decoration ? And I have no idea about which versions of Ubuntu are concerned. And if it is only a Unity problem, I don't even know how to find out if my user is using Unity...
Any idea for a workaround ?
Edit :
Ok, MadProgrammer did provide a better code, but the bug still occurs sometimes. I edited my MouseListener accordingly to track the bug :
pane.addMouseMotionListener(new MouseMotionListener() {
private int posX = 0, posY = 0;
#Override
public void mouseDragged(MouseEvent e) {
int x = e.getXOnScreen() - posX;
int y = e.getYOnScreen() - posY;
mainFrame.setLocation(x, y);
System.out.println("drag : border ignored / border considered : "+(mainFrame.getY()+e.getY())+" / "+e.getYOnScreen());
}
#Override
public void mouseMoved(MouseEvent e) {
posX = e.getXOnScreen() - mainFrame.getX();
posY = e.getYOnScreen() - mainFrame.getY();
System.out.println("move : border ignored / border considered : "+e.getY()+" / "+posY);
}
});
Each time that the 2 values are identical, it means that the bug will occur on the next click. Otherwise, the values are different. On other OS, the values are always the same. Actually, they should be or the same always, or always different. I don't understand how they can be sometimes equal and sometimes different...
I don't have Ubuntu to test with, but I've used something similar to this on both MacOS and Windows
import java.awt.Container;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Test {
private static JFrame mainFrame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
mainFrame = new JFrame("test");
mainFrame.setSize(300, 100);
mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
mainFrame.setVisible(true);
Container pane = mainFrame.getContentPane();
MouseAdapter ma = new MouseAdapter() {
private Point offset;
#Override
public void mouseDragged(MouseEvent e) {
if (offset != null) {
Point pos = e.getLocationOnScreen();
int x = pos.x - offset.x;
int y = pos.y - offset.y;
System.out.println(x + "x" + y);
SwingUtilities.getWindowAncestor(e.getComponent()).setLocation(x, y);
}
}
#Override
public void mousePressed(MouseEvent e) {
Point pos = SwingUtilities.getWindowAncestor(e.getComponent()).getLocation();
// Point pos = e.getComponent().getLocationOnScreen();
offset = new Point(e.getLocationOnScreen());
System.out.println(pos + "/" + offset);
offset.x -= pos.x;
offset.y -= pos.y;
System.out.println(offset);
}
};
pane.addMouseListener(ma);
pane.addMouseMotionListener(ma);
}
});
}
}
This should work for both decorated and undecorated windows, as it takes the difference between the positions of the component (on the screen) and the windows current position. When dragged, it calculates the distance of movement from the click point and updates the window's location accordingly (allowing for the original offset of the click)
I have an assignment that I am doing where I am supposed to implement and design an application that plays a game called catch the creature. Have the creature appear at a random location then disappear and reappear somewhere else. The goal is to "catch" the creature by clicking the creature with a mouse button. Record the number of times the creature is caught.
I need help just displaying the creature which is an JPEG of a pikachu, I have tried a few things but none of them work. Any help is appreciated thank you!
Main Code:
import javax.swing.*;
public class Catch_The_Creature
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Catch the Creature");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Creature panel = new Creature();
JOptionPane.showMessageDialog(frame, "Catch Pikachu!");
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
Creature Code:
import java.awt.*;
import java.util.Random;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class Creature extends JPanel
{
private final int WIDTH = 400, HEIGHT = 300;
private final int DELAY=20, IMAGE_SIZE = 60;
private ImageIcon image;
private int pikachuX, pikachuY;
private int x, y;
private int catchCount=0;
private static Random generator = new Random();
private Timer time;
private ActionListener updater;
private JLabel countLabel;
public Creature()
{
image = new ImageIcon("image/pikachu.jpg");
time = new Timer(DELAY, updater);
addMouseListener ((MouseListener) new MouseClickedListener());
setBackground (Color.green);
setPreferredSize(new Dimension(1900,1000));
time.start();
}
public boolean point(int x, int y)
{
if (x == pikachuX && y == pikachuY)
{
catchCount++;
return true;
}
return false;
}
public int getCatchCount()
{
return catchCount;
}
private class MouseClickedListener extends MouseAdapter
{
public void mouseClicked(MouseEvent event)
{
point(event.getX(), event.getY());
}
}
public void paintComponent(Graphics page)
{
super.paintComponent(page);
page.drawImage(image.getImage(),WIDTH, HEIGHT, null);
page.drawString("Pikachus Captured: " + catchCount, 10, 35);
setFont(new Font("Arial", Font.BOLD,35));
}
public void actionPerformed(ActionEvent event)
{
time.setDelay(1000);
x += pikachuX;
y += pikachuY;
if (x <= 0 || x >= WIDTH-IMAGE_SIZE)
pikachuX = pikachuX * -1;
if (y <= 0 || y >= HEIGHT-IMAGE_SIZE)
pikachuY = pikachuY * -1;
repaint();
}
public void mouseEntered(MouseEvent arg0) {}
public void mouseExited(MouseEvent arg0) {}
public void mousePressed(MouseEvent arg0) {}
public void mouseReleased(MouseEvent arg0){}
}
It doesn't look like you ever add the ImageIcon to the panel or tell it to paint in the paintComponent() method.
First solution [Preferred]: Add ImageIcon to the panel. In the constructor
super.add(image);
Make sure you use the correct layout (probably a null or absolute layout) and that you update the coordinates of the ImageIcon itself, not just some member variables.
Second solution: Paint the ImageIcon in the paintComponent() method. This is probably discouraged because it goes against the general Swing principles.
Make sure your Image file is in the right directory. If you're running from netbeans or eclipse your file structure should look like this
ProjectRoot
src
bin
image
pikachu.jpeg
Since you are using "image/pikachu.png", you image filder should be a child of the project root folder as that's where the IDE will first search fore your file path
Edit: To draw image. Instead of using ImageIcon, use BufferedImage
try {
BufferedImage image = ImageIO.read("image/pikachu.jpeg");
} catch (Exception ex){
ex.printStackTrace();
}
public void paintComponent(Graphics page)
{
super.paintComponent(page);
page.drawImage(image, x, y, heightYouWant, widthYouWant, this);
page.drawString("Pikachus Captured: " + catchCount, 10, 35);
setFont(new Font("Arial", Font.BOLD,35));
}
All i needed to do was put values on where I wanted the picture to start at in the constructor.
public Creature()
{
image = new ImageIcon ("pikachu.png");
time = new Timer(DELAY, updater);
x = 0;
y = 50;
addMouseListener ((MouseListener) new MouseClickedListener());
setBackground (Color.green);
setPreferredSize(new Dimension(1900,1000));
time.start();
}
While still using an Image Icon and still paint the image in the paint component.
public void paintComponent(Graphics page)
{
super.paintComponent(page);
image.paintIcon (this, page, x, y);
page.drawString("Pikachus Captured: " + catchCount, 10, 35);
setFont(new Font("Arial", Font.BOLD,35));
}
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class Panel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
final static int WIDTH = 800;
final static int HEIGHT = 600;
private int x = 40, y = 49, r = 20;
Dimension SIZE = new Dimension(WIDTH, HEIGHT);
public Panel() {
setLayout(new BorderLayout());
setPreferredSize(SIZE);
setMaximumSize(SIZE);
setMinimumSize(SIZE);
setBackground(Color.cyan);
setFocusable(true);
requestFocus();
new input(this);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.fillOval(x, y, r, r);
repaint();
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
}
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class input extends KeyAdapter {
private Panel panel;
public input(Panel panel) {
panel = new Panel();
panel.addKeyListener(this);
}
#Override
public void keyPressed(KeyEvent e) {
int keycode = e.getKeyCode();
int x = panel.getX();
int y = panel.getY();
if (keycode == KeyEvent.VK_LEFT) {
panel.setX(x - 1);
}
if (keycode == KeyEvent.VK_RIGHT) {
panel.setX(x + 1);
}
}
}
I'm a Java newbie. I was trying to make a class specially for KeyListener
but it just doesn't work. I can't figure out what I did wrong.
It might be meaningless to make a inputhandler class, and it controls only the only one class (jpanel), but i used to put all of my code in one single class.. it looks so bad. i'm learning to make them into more separate classes or make them more object-oriented~
I just confuse when to make a new class, and when not.
please help me, could you tell me what i did wrong with my code above. Was my thought wrong or just the code?
This looks like the first reason your KeyListener doesn't work:
public input(Panel panel) {
panel = new Panel();
panel.addKeyListener(this);
}
When you do this:
new input(this);
Because you are also doing this:
panel = new Panel();
You are adding the KeyListener to new Panel(), not the panel you are passing in to the constructor with this.
Personally I don't see a problem with creating a listener object just to listen, although I do not think that is necessary here.
Although presumably this will not have an effect on your listener, one other problem I see is that you are overriding paint which you should not be doing in Swing. You should be overriding paintComponent. See Painting in AWT and Swing: http://www.oracle.com/technetwork/java/painting-140037.html#callbacks
Also as far as style goes, class names start with a capital letter in Java. Your input class should be named Input.