I am pretty overwhelmed at the moment with this assignment. I am only 3 weeks into Java programming and I was given this code and told to modify it in ways I'm not exactly familiar with. So any help would be greatly appreciated. I really want to understand this, so any information would also be helpful.
The directions that I'm struggling with at the moment are:
"Modify the Model class to store an ArrayList of Turtles. Make
Model.update call Turtle.update for each Turtle in the ArrayList. To
test it, put two or three turtles in the ArrayList, each starting in
different locations. When you click (which causes "setDestination" to
be called), make all of the turtles head for the same destination. Get
this much working before you proceed."
Before I created an array of these turtles, one turtle showed up fine. Now that I have created an array, nothing is showing up on my panel. It does compile properly, though. I understand that I should make separate Graphics objects, but how and where would be the best place to do this? Here is the code (which was in 4 separate files):
Controller.java
import java.awt.Graphics;
import java.io.IOException;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import javax.swing.Timer;
class Controller implements MouseListener
{
Model model;
View view;
Controller() throws IOException, Exception {
model = new Model();
view = new View(this);
new Timer(50, view).start();
}
public void update(Graphics g) {
model.update(g);
}
public void mousePressed(MouseEvent e) {
model.setDestination(e.getX(), e.getY(), view.getWidth(), view.getHeight());
}
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) { }
public static void main(String[] args) throws Exception {
// Use the following line to determine which directory your program
// is being executed from, since that is where the image files will
// need to be.
//System.out.println("cwd=" + System.getProperty("user.dir"));
new Controller();
}
}
Model.java
import java.awt.Graphics;
import java.io.IOException;
class Model
{
private Turtle[] t;
Model() throws IOException {
Turtle[] t = new Turtle[3];
for (int i = 0; i <3; i++)
t[i] = new Turtle();
// turtle = new Turtle();
}
public void update(Graphics g) {
for(int i = 0; i <3; i++)
t[i].update(g);
}
public void setDestination(int x, int y, int width, int height) {
for(int i = 0; i <3; i++)
{
t[i].setDest(x, y);
}
}
}
Turtle.java
import java.awt.Graphics;
import java.awt.Image;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.util.Random;
class Turtle
{
private int x;
private int y;
private int dest_x;
private int dest_y;
private Image image;
Turtle() {
try {
image = ImageIO.read(new File("turtle.png"));
} catch (IOException ioe) {
System.out.println("Unable to load image file.");
}
}
public int getX() { return x; }
public int getY() { return y; }
public void setX(int xIn) { x = xIn; }
public void setY(int yIn) { y = yIn; }
public void update(Graphics g) {
// Move the turtle
if (x < dest_x) {
x += 1;
} else if (x > dest_x) {
x -= 1;
}
if (y < dest_y) {
y += 1;
} else if (y > dest_y) {
y -= 1;
}
// Draw the turtle
}
public void setDest(int x, int y) {
dest_x = x;
dest_y = y;
}
}
View.java
import javax.swing.JFrame;
import java.awt.Graphics;
import javax.swing.JPanel;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class View extends JFrame implements ActionListener {
private class MyPanel extends JPanel {
Controller controller;
MyPanel(Controller c) {
controller = c;
addMouseListener(c);
}
public void paintComponent(Graphics g) {
controller.update(g);
revalidate();
}
}
public View(Controller c) throws Exception{
setTitle("Assignment 4");
setSize(1000, 700);
getContentPane().add(new MyPanel(c));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public void actionPerformed(ActionEvent evt) {
repaint();
}
}
I'm positively surprised by the code supplied by the teacher; usually it's pretty bad, but this one is decent. Although you need to modify only one class, allow me to comment a bit more because "any information would also be helpful".
Controller
class Controller extends MouseAdapter
instead of
class Controller implements MouseListener
allows you to get rid of all the empty methods
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) { }
(And in case you need to extend controller with something else just make an inner class extending MouseAdapter and use that.)
Turtle
Should expose a constructor such as
Turtle(int x, int y) {
this();
this.x = x;
this.y = y;
}
so that
Turtle t = new Turtle();
t.setX(4);
t.setY(6);
could be replaced with
new Turtle(4, 6);
Furthermore, dest_x and dest_y should be renamed to something like destX and destY since underscore (_) is used only in names of final instances.
Lastly, you need to replace the line
// Draw the turtle
with
g.drawImage(image, x, y, null);
View
The dimensions specified in
setSize(1000, 700);
should be defined as fields (don't scatter arbitrary numbers across the code)
int sizeX = 1000, sizeY = 700;
...
setSize(sizeX, sizeY);
and should be exposed so that when choosing initial location for the turtles they won't start out of bounds.
Model
You were asked: Modify the Model class to store an ArrayList of Turtles, but you created an array instead for some reason. Here is an example of what you can do to make it work:
public class Model {
private List<Turtle> turtList = new ArrayList<>();
private int turtleNumber = 3;
Model() throws IOException {
for (int i = 0; i < turtleNumber; i++) {
Turtle turt = new Turtle();
turt.setX(Math.round((float)Math.random() * 1000));
turt.setY(Math.round((float)Math.random() * 700));
turtList.add(turt);
}
}
public void update(Graphics g) {
for (int i = 0; i < turtleNumber; i++)
turtList.get(i).update(g);
}
public void setDestination(int x, int y, int width, int height) {
for (Turtle turt : turtList)
turt.setDest(x, y);
}
}
I define the number of turtles I want as a field and use it throughout the code so I need to change it only in one place when I change the number.
In the constructor, I loop using the given turtle constructor (a null constructor) and randomly place them inside the frame's bounds (they should be read from View's sizeX and sizeY). Then I add the modified turtle to the list.
For instructional purpose, I used two different looping constructs for update and for setDestination. Choose whichever you want (just notice that you don't need to know the number of turtles).
In the constructor of your Model class you have the line
Turtle[] t = new Turtle[3];
You also have the class variable,
Turtle[] t
but are not assigning the array initialised in the Constructor to the class variable, so it is only considered for the scope of that method and hence why nothing is drawn, as when you go to use the variable t later it has not been initialised and so you get NullPointerExceptions
To fix this simply change the line
Turtle[] t = new Turtle[3];
to
t = new Turtle[3];
Now this will only appear as one image, but is actually 3 turtles overlaid on top of each other. So to fix this you will need some extra code to put the turtles in different starting positions, but i'll leave you to figure this part out.
Related
Okay, so I'm trying to make a program that draws a bunch of rectangles that move around the screen randomly. I have a Dot class where each dot holds its x and y values, and in my paint class I randomly change the x and y values and then repaint(). What I have right now doesn't load any thing other than a blank JFrame. I suspect that I'm drawing each dot wrong. The following is my code:
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Movement extends JFrame {
public ArrayList<Dot> dots = new ArrayList<Dot>();
Random rn = new Random();
DrawPanel drawPanel = new DrawPanel();
public Movement() {
for(int i = 0; i < 100; i ++) {
Dot dot = new Dot(5, 5);
dots.add(dot);
}
ActionListener listener = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < dots.size();i++) {
dots.get(i).setX(dots.get(i).getX() + rn.nextInt(20)-10);
dots.get(i).setY(dots.get(i).getY() + rn.nextInt(20)-10);
}
drawPanel.repaint();
}
};
Timer timer = new Timer(100, listener);
timer.start();
add(drawPanel);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
setBounds(100, 100, 500, 500);
}
private class DrawPanel extends JPanel {
protected void paintComponent(Graphics g) {
for(int i = 0; i < dots.size(); i ++) {
g.fillRect(dots.get(i).getX(), dots.get(i).getY(), 5, 5);;
super.paintComponent(g);
}
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new Movement();
}
});
}
}
Dot class:
public class Dot {
private int x, y;
public Dot(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
Any and all help is appreciated.
If you call super.paintComponent(g); after you paint your own components you have wiped out your own painting. So,
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
for(int i = 0; i < dots.size(); i ++) {
g2d.fillRect(dots.get(i).x, dots.get(i).y, 5, 5);
}
}
Also,
// don't repeat type in constructor
// use built in point instead of custom class
public ArrayList<Point> dots = new ArrayList<>();
and
ActionListener listener = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < dots.size();i++) {
dots.get(i).x = dots.get(i).x + rn.nextInt(20)-10;
dots.get(i).y = dots.get(i).y + rn.nextInt(20)-10;
}
drawPanel.repaint();
}
};
and it probably doesn't make any difference, but
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Movement mf = new Movement();
}
});
You appear to be calling super.paintComponent after you paint dots. Not only that, you appear to be calling it inside the loop, calling it every time you paint a dot, after you paint it.
So you keep painting a dot, letting super.paintComponent paint a clear panel, then painting another dot, then undoing it again, paint another, undo... and so on.
Call super.paintComponent one time, and do it before you do your custom painting.
This is my frog class which is being called by a GUI class.
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
public class Frog implements Runnable {
private int x, y, dx, dy;
private int coordinates[]=new int[2];
private boolean hungry;
private JLabel jLabel;
private ImageIcon image;
private String name;
public Frog() {
}
public Frog(String name, boolean hungry) {
image = new ImageIcon("images/frog.jpeg");
jLabel = new JLabel(image);
setName(name);
setHungry(hungry);
setCoordinates();
}
private void setName(String name) {
this.name=name;
}
public int[] getCoordinates() {
return coordinates;
}
public boolean isHungry() {
return hungry;
}
public void setHungry(boolean hungry) {
this.hungry = hungry;
}
public void display(Graphics paper) {
paper.setColor(Color.black);
paper.drawOval(x, y, dx, dx);
}
public void setCoordinates() {
for(int i = 0; i < 2; i++) {
Random rand = new Random();
int p = rand.nextInt(100);
coordinates[i] = p;
}
setX(coordinates[0]);
setY(coordinates[1]);
}
public void move() {
x = (int)Math.random() * 350;
y = (int)Math.random() * 350;
dx=20;
dx=20;
x += dx;
y += dy;
if (x > 800 || x < 0)
dx=-dx;
if (y > 600 || y < 0)
dy=-dy;
}
public void setDx(int dx) {
this.dx = dx;
}
public void setDy(int dy) {
this.dy = dy;
}
public void setX(int x){
this.x = x;
}
public void setY(int y){
this.y = y;
}
public void run() {
while(!hungry) {
move();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I have created this class to move a frog object randomly but when I call the move method nothing is happening. I want the frog on a JPanel to randomly move around the screen. I have another class which uses the frog object.
It is used from a main class containing the following:
public void actionPerformed(ActionEvent event) {
if (event.getSource() == MakePet) {
String petName = namePlace.getText();
frog = new Frog(petName,false);
panel.add(pet);
panel.add(prey);
frog.move();
Thread fro = new Thread(frog);
fro.start();
}
}
public static void main(String[] args) {
GUI demo = new GUI();
demo.setSize(520,720);
demo.createGUI();
demo.show();
}
For an example of timer based animation, have a look at http://www.java2s.com/Code/Java/2D-Graphics-GUI/Timerbasedanimation.htm
Another example of timer based animation, where Swing components are used to draw images on a JPanel was given Jérôme's comment: Randomly moving images on a JPanel
If you require having multiple frogs with their own threads of control, then you will need to handle synchronization. Otherwise I would simply call move from the timer before repainting the panel. And then change move:
public void move() {
if (!hungry) {
return;
}
...
}
Set the timer's interval appropriately based on the framerate you are looking to achieve. The below is based on the answer from the question Jérôme linked to:
Timer t = new Timer(1000 / DESIRED_FRAMERATE, (event) -> {
frogs.forEach(Frog::move);
panel.repaint();
});
Note that this is Java 8 using lambdas.
The above in pre-Java 8:
Timer t = new Timer(1000 / DESIRED_FRAMERATE, new ActionListener() {
public void actionPerformed(ActionEvent event) {
for (Frog frog : frogs) {
frog.move();
}
panel.repaint();
}
});
You should create the Timer in the initialization code of your JFrame or JPanel. If you have a class extending JPanel, you don't need to reference panel when calling repaint if the Timer is inside that class. Also, here I am guessing you have a collection of Frog objects, typically a list, called frogs. If there's only ever one frog, you don't need the loop. Frog's display method needs to be called from the JPanel's paint method, so I am guessing you have a class extending JPanel.
I can draw static things to the screen, but I want to make them move with user key input. I don't know what to do, I've been searching and searching and haven't come up with an answer yet. Please help!
package com.Game.game;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JFrame
{
final static int width = 500;
final static int height = 500;
public int x = 250;
public int y = 250;
public int changeX = 10;
public int changeY = 10;
public static void main(String[] args)
{
new Game();
}
public Game()
{
KeyListener listener = new KeyListening();
addKeyListener(listener);
setFocusable(true);
DrawingStuff drawingstuff = new DrawingStuff();
add(drawingstuff);
setSize(width, height);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public class DrawingStuff extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawString("Hey there!", 300, 300);
g.setColor(Color.RED);
g.fillRect(x, y, 50, 50);
}
}
public class KeyListening implements KeyListener
{
DrawingStuff drawingstuff = new DrawingStuff();
#Override
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_UP)
{
y = y + changeY;
System.out.println("Hey");
drawingstuff.repaint();
}
}
#Override
public void keyReleased(KeyEvent e)
{
}
#Override
public void keyTyped(KeyEvent e)
{
}
}
public void update()
{
}
}
EDIT: Fixed it. I took away the key listener stuff in the constructor method, added a command to focus on "drawingstuff" in the constructor method, and, most importantly, added this bit of code to the end of the constructor method:
while(true)
{
drawingstuff.repaint();
}
The problem is that your KeyListening object has a reference to a different DrawingStuff object than the one you added to your UI inside the Game constructor.
public class KeyListening implements KeyListener
{
DrawingStuff drawingstuff = new DrawingStuff();
...
You should pass a DrawingStuff reference to the KeyListening instance so that it can tell the right object to repaint itself.
I'm currently working on a map generation program for a game I'm creating, and so I'm trying to be able to modify the individual tiles on the map using my mouse. I've successfully implemented the ability to click on a tile and change its value (from say a 4 way tile to a 2 way tile), but I am having trouble getting mouseDragged to work. According to the Java docs I've done the correct implementation of the mouseDragged interface into my MouseEventHandler, but I when I drag my mouse and move my mouse around neither of those events trip and throw the println text that I've got them throwing right now. If anyone could shed some light as to why this isn't working for me, I'd love that. Thanks!
A side question: If anyone has any better methods of trying to generate a map like this, or any (presumably anything is better than what I did) other methods of detecting individual mousePressed on a tile, I'd love to hear it. This is only my first time working with this sort of thing so I'm quite inexperienced and would love some advice.
As for the code itself:
File 1: OneQuestMapgen.java
package OneQuestMapgen;
import java.awt.Graphics;
import java.util.ArrayList;
import javax.swing.*;
import tiles.Tile;
import tiles.TileSet;
public class OneQuestMapgen extends JApplet{
/**
*
*/
public static OneQuestMapgen instance;
ArrayList<ArrayList<Tile>> map = new ArrayList<ArrayList<Tile>>();
TileSet tileSet = new TileSet();
public void init(){
this.setSize(950,600);
}
public void start(){
this.addMouseListener(new MouseEventListener(this));
int tileSize = tileSet.get("grasslands")[1].getHeight();
for (int i = 0; i < getHeight(); i += tileSize) {
ArrayList<Tile> temp = new ArrayList<Tile>();
for (int j = 0; j < getWidth(); j += tileSize) {
temp.add(new Tile(j, i, tileSize, tileSet.get("grasslands")));
}
map.add(temp);
}
}
public void paint(Graphics g){
for (int i = 0; i < map.size(); i++){
for(int j = 0; j < map.get(i).size(); j++){
map.get(i).get(j).render(g);
}
}
}
public void stop(){
}
public void destroy(){
}
public ArrayList<ArrayList<Tile>> getMap(){
return map;
}
}
File 2: MouseEventListener.java
package OneQuestMapgen;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import tiles.Tile;
public class MouseEventListener implements MouseListener, MouseMotionListener {
OneQuestMapgen instance;
ArrayList<ArrayList<Tile>> map;
public MouseEventListener(OneQuestMapgen instance) {
this.instance = instance;
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println(e.getX() + " , " + e.getY());
map = instance.getMap();
for (int i = 0; i < map.size(); i++) {
for (int j = 0; j < map.get(i).size(); j++)
if (map.get(i).get(j).bounds(e)) {
map.get(i).get(j).onClicked(instance.getGraphics());
}
}
}
#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) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseDragged(MouseEvent arg0) {
System.out.println("Mouse Dragged: " + arg0.getX() + " , " + arg0.getY());
}
#Override
public void mouseMoved(MouseEvent arg0) {
// TODO Auto-generated method stub
System.out.println("Mouse Moved: " + arg0.getX() + " , " + arg0.getY());
}
}
File 3: Tile.java
package tiles;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
#SuppressWarnings("serial")
public class Tile extends Square {
BufferedImage[] tiles;
int tileValue = 0;
public Tile (int x, int y, int size, BufferedImage[] tileSet){
super(x,y,size);
tiles = tileSet;
}
public void render(Graphics g) {
g.drawImage(tiles[tileValue], getX(), getY(), getDimension(), getDimension(), null);
}
public void onClicked(Graphics g) {
tileValue++;
System.out.println(tileValue);
g.drawImage(tiles[tileValue], getX(), getY(), getDimension(), getDimension(), null);
}
public boolean bounds(MouseEvent e){
if (e.getX() <= getX() + getDimension() && e.getX() >= getX() && e.getY() <= getY() + getDimension() && e.getY() >= getY())
return true;
return false;
}
}
File 4: TileSet.java
package tiles;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
import javax.imageio.ImageIO;
public final class TileSet {
ConcurrentHashMap<String, BufferedImage[]> container = new ConcurrentHashMap<String, BufferedImage[]>(11);
BufferedImage[] grasslands = new BufferedImage[11];
public TileSet(){
boolean toAdd = true;
try {
grasslands[0] = ImageIO.read(new URL("http://i675.photobucket.com/albums/vv118/va023/OneQuest/GrassBlank_zpse99de845.png"));
grasslands[1] = ImageIO.read(new URL("http://i675.photobucket.com/albums/vv118/va023/OneQuest/4WayTile_zps49ebbeea.png"));
grasslands[2] = ImageIO.read(new URL("http://i675.photobucket.com/albums/vv118/va023/OneQuest/LineHorizontal_zpsc7bd45d5.png"));
grasslands[3] = ImageIO.read(new URL("http://i675.photobucket.com/albums/vv118/va023/OneQuest/LineVertical_zps9719b63f.png"));
grasslands[4] = ImageIO.read(new URL("http://i675.photobucket.com/albums/vv118/va023/OneQuest/TShapeTop_zpsa4b2aaa1.png"))
}
catch (Exception e) {
System.out.println("Unable to load resources for Grasslands");
}
for (int i = 0; i < grasslands.length; i++)
if (grasslands[i].getWidth() != grasslands[i].getHeight()) {
System.out.println("Error, tiles are not square. Grasslands has not been added.");
toAdd = false;
}
if (toAdd) {
container.put("grasslands".toLowerCase(), grasslands);
}
}
public BufferedImage[] get(String s) {
return container.get(s.toLowerCase());
}
}
File 5: Square.java
package tiles;
import javax.swing.JLabel;
public abstract class Square extends JLabel{
/**
*
*/
private static final long serialVersionUID = 1L;
private int x;
private int y;
private int dimension;
public Square(int x, int y, int dimension){
this.x = x;
this.y = y;
this.dimension = dimension;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public int getDimension(){
return dimension;
}
}
Note: If you would rather be able to pull it all off of github, I've added a link to my github repo for this project below:
URL: https://github.com/vagrawal1/OneQuestMapgen
HTTPS: https://github.com/vagrawal1/OneQuestMapgen.git
SSH: git#github.com:vagrawal1/OneQuestMapgen.git
Git Read-Only: git://github.com/vagrawal1/OneQuestMapgen.git
Thanks once again for your help and thank you for taking the time to read this.
You need to add a MouseMotionListener for dragging to work. You're just adding a MouseListener.
i.e.,
public void start() {
MouseEventListener myMouseEventListener = new MouseEventListener(this);
addMouseListener(myMouseEventListener);
addMouseMotionListener(myMouseEventListener);
//this.addMouseListener(new MouseEventListener(this));
Also as per my comments, your getX() and getY() methods of the Square class override the same methods of JLabel, and that if you aren't careful with these and use them correctly they can totally mess up the placement of your components in the GUI. Myself, I take great pains to avoid overriding these very methods. Consider changing their names if overriding wasn't your intention.
Also, I tend to favor mousePressed(...) over the mouseClicked(...) method as the former will respond to the press even if you move the mouse after pressing it.
Edit: another bug! You should almost never get your Graphics instance by calling getGraphics() on a component as the Graphics object returned is evanescent and may soon become null. Instead either draw on a BufferedImage which is displayed in a JComponent or JPanel's paintComponent(...) or just draw in paintComponent(...) itself.
Edit 2: why override paint(...)? That seems a bit dangerous to me, especially since you're not calling the super method, and especially since there is no need to do this sort of thing.
Hello people,
I am trying to write an animated character for a multitouch screen. I want my object to have 5 eyes and each of whose pupil to be dragged and dropped differently, within the eye of course.
I have tried to do it all in a single class and the problem seems to be assigning mouse handlers to each of the five pupils! In other words, if I move one pupil, all the pupils are moving.
Then, I resorted to using a bespoke class just to the pupil. When I use it by itself, the pupil is draggable. However, when I use it as an object in the eyes class, the pupil is static! No clicks registered, no mouse activity tracked.
I have looked at tutorials, other related issues with mouse handlers and could not make any progress. I changed the code a dozen times from various tutorials and suggestions before finally posting here. Any ideas where I am missing the cue? Any pointers would be greatly appreciated.
Thanks in advance.
PS: I know I am yet to put the constraints on the pupil movement within the eye.
Code for main eyes class:
package rollEyes;
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class FiveEyes extends JPanel
{
private static final long serialVersionUID = 1L;
private static final int SIZE = 512;
private int a = SIZE / 2;
private int b = a;
private int r = 4 * SIZE / 5;
private int n;
int circleSize=30;
Pupil dc = new Pupil(1);
public FiveEyes(int n)
{
super(true);
this.setPreferredSize(new Dimension(SIZE, SIZE));
this.n = n;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.black);
a = getWidth() / 2;
b = getHeight() / 2;
int m = Math.min(a, b);
r = 4 * m / 5;
int r2 = Math.abs(m - r) / 2;
int numOfEyes = 5;
for (int i = 0; i < numOfEyes ; i++)
{
Graphics2D g2d2 = (Graphics2D) g;
double t = 2 * Math.PI * i / n;
int x = (int) Math.round(a + r * Math.cos(t));
int y = (int) Math.round(b + r * Math.sin(t));
drawEyeSockets(g2d2, x,y, 2*r2,2*r2);
}
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
create();
}
});
}
public void drawEyeSockets(final Graphics2D g2, int x, int y, int w, int h)
{
g2.drawOval(x,y,w,h);
dc.drawCircle(g2, x+12, y+12);
}
private static void create()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FiveEyes fivey = new FiveEyes(5);
f.add(fivey);
f.pack();
f.setVisible(true);
}
}
Code for the Pupil class:
package rollEyes;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Pupil extends JPanel
{
private static final long serialVersionUID = 1L;
int radius=50;
int x_after = 50;
int y_after = 50;
MouseHandler mh ;
private static int n =1;
public Pupil(int n)
{
super(true);
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
drawCircle(g2d,x_after,y_after);
}
public void drawCircle(final Graphics2D g2d, int x, int y)
{
g2d.setColor(Color.BLUE);
g2d.fillOval(x, y, radius/2, radius/2);
mh = new MouseHandler();
this.addMouseListener(mh);
this.addMouseMotionListener(mh);
}
private static void create()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Pupil dc = new Pupil(n);
f.add(dc);
f.pack();
f.setVisible(true);
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
create();
}
});
}
private class MouseHandler extends MouseAdapter
{
boolean circleClicked=false;
public void mouseReleased(MouseEvent e)
{
circleClicked = false;
}
public void mousePressed(MouseEvent me)
{
circleClicked = true;
}
public void mouseDragged(MouseEvent me)
{
if (circleClicked)
{
x_after = me.getX();
y_after = me.getY();
repaint();
}
}
}
}
You have Pupil extend JPanel, but really shouldn't be doing that. Instead, use the concepts that you've learned in your current Pupil class -- how to draw a movable circle, and extend it in the larger FiveEyes class, only this time create a List<Pupil> and draw them. My suggestions:
Make Pupil not extend JPanel. Instead give it the machinery to draw circles in certain locations and to have that location changed.
Also you will need to give it a way to recognize if its circle has been clicked by giving it a contains(Point p) method. One way to do this is to use a Shape object, or you can roll your own method.
Give FiveEyes a List<Pupil> that in reality is an ArrayList<Pupil> and fill it with Pupil objects.
In FiveEyes paintComponent(...) method, iterate through this List telling each Pupil to draw itself.
In your FiveEyes MouseAdapter's mousePressed(...) method, iterate through your Pupil List to see if a Pupil has been clicked on. If so, move it.
Alternatively, you could create a Pupil BufferedImage, put it into an ImageIcon, and put that into a JLabel, and then allow your FiveEyes class's MouseAdapter to drag the labels around.
First of all, this code
mh = new MouseHandler();
this.addMouseListener(mh);
this.addMouseMotionListener(mh);
must only be called once. You're adding millions of mouse handlers to the component!
And you need five instances of Pupil, one for each eye. Right now, you have only one, so of course the rendering will only yield one result.
Lastly, you must make sure that only one eye is "active" at a time (i.e. not all of them should receive mouse events or not all of them should process them).