Write a public instance method move() that takes two integer arguments representing the amount by which to change the values of the instance variables xPos and yPos. The method should return no value. It should make use of the provided method delay() to pause so that the effects of running the method repeatedly are visible, e.g. this.delay(20);
pauses execution for 20 ms.
Test your code by moving an instance of StickFigure and checking that it remains aligned.
My code I have is below but I cannot seem to figure out on how to move all 3 shapes together, only the triangle seems to move.
public class StickFigure
{
/*Instance variables*/
private int xPos;//The horizontal position of a StickFigure
private int yPos;//The vertical position of a StickFigure
private Circle head;
private Triangle body;
private Rectangle leg;
// add your declarations here for part (a)(i)
/**
* Constructor for objects of class StickFigure that
* provides a default stick figure near the bottom left corner of the graphical display.
*
*/
public StickFigure()
{
super();
this.head = new Circle (30, OUColour.PINK);
this.body = new Triangle (50, 50, OUColour.RED);
this.leg = new Rectangle (6, 50, OUColour.PINK);
this.setXPos(25); //sets starting position towards bottom left of Shapes window
this.setYPos(220);
this.alignAll();
}
/**
* Sets the xPos of the receiver to the value of the argument.
*/
public void setXPos(int newPos)
{
this.xPos = newPos;
this.body.setXPos(newPos);
this.alignAll();
//part (b)(iii)
}
/**
* Returns the xPos of the receiver.
*/
public int getXPos()
{
return this.xPos;
}
/**
* Sets the yPos of the receiver to the value of the argument.
*/
public void setYPos(int newPos)
{
this.yPos = newPos;
this.body.setYPos(newPos);
this.alignAll();
//part (b)(iii)
}
/**
* Returns the yPos of the receiver.
*/
public int getYPos()
{
return this.yPos;
}
/**You will need to uncomment these methods when directed to*******/
/**
* Returns a reference to the head of the receiver.
*/
public Circle getHead()
{
return this.head;
}
/**
* Returns a reference to the body of the receiver.
*/
public Triangle getBody()
{
return this.body;
}
/**
* Returns a reference to the leg of the receiver.
*/
public Rectangle getLeg()
{
return this.leg;
}
/**
* Aligns the head of the receiver relative to the xPos and yPos of the body.
*/
public void alignHead()
{
this.head.setXPos(this.body.getXPos() + (this.body.getWidth() - this.head.getDiameter())/2);
this.head.setYPos(this.body.getYPos() - this.head.getDiameter());
}
/**
* Aligns the body of the receiver relative to the xPos and yPos of the head.
*/
public void alignBody()
{
this.body.setXPos(25);
this.body.setYPos(220);
}
/**
* Aligns the leg of the receiver relative to the xPos and yPos of the head and body.
*/
public void alignLeg()
{
this.leg.setXPos(this.body.getXPos() + (this.body.getWidth() - this.leg.getWidth())/2);
this.leg.setYPos(this.body.getYPos() + this.leg.getHeight());
}
/**
* Aligns all the body parts of the receiver to form the
* StickFigure-type figure.
*/
public void alignAll()
{
this.alignBody();
this.alignHead();
this.alignLeg();
}
public void move(int xPos, int yPos)
{
this.alignAll();
this.body.setXPos(xPos + xPos);
this.body.setYPos(yPos + yPos);
this.delay(20);
}
Shouldn't your move() set the xPos and yPos. so:
this.body.setXPos(xPos + xPos);
this.body.setYPos(yPos + yPos);
should be
this.setXPos(this.xPos + xPos);
this.setYPos(this.yPos + yPos);
followed by alignAll()
Then your alignBody() says: "Aligns the body of the receiver relative to the xPos and yPos of the head" but your method doesn't do that ...
It seems the head/leg are relative to body. So maybe the bodies X, Y should be relative to the xPos and yPos?
Related
Right I'm working with libGDX/Box2D at the moment and I am completely stumped by a bug I discovered while following this set of tutorials libGDX.
In my game I create an actor and add it to my game stage. The actor is placed on the screen at a given y coordinate. The game simply loops through an animation to simulate running.
The problem is, when I place the actor on the game stage it's y coordinate is somehow given the wrong y coordinate, retrieved using the following code snippet.
body.getPosition().y
The y coordinate of the actor changes from the correct position of 2.5 to an incorrect position of 3.6 on game startup. The actor then operates as normal from it's updated (incorrect) y position.
I've debugged the bejaysus of my code and I can't see anything that would be causing this. Has anyone come across this kind of behaviour before? Is this some kind of Box2D nuance I am not aware of?
Game Actor Class
public abstract class GameActor extends Actor {
protected Body body;
protected UserData userData;
protected Rectangle screenRectangle;
public GameActor() {
}
public GameActor(Body body) {
this.body = body;
this.userData = (UserData) body.getUserData();
screenRectangle = new Rectangle();
}
#Override
public void act(float delta) {
super.act(delta);
if (body.getUserData() != null) {
updateRectangle();
} else {
// This means the world destroyed the body (enemy or runner went out of bounds)
remove();
}
}
public abstract UserData getUserData();
private void updateRectangle() {
screenRectangle.x = transformToScreen(body.getPosition().x - userData.getWidth() / 2);
screenRectangle.y = transformToScreen(body.getPosition().y - userData.getHeight() / 2);
screenRectangle.width = transformToScreen(userData.getWidth());
screenRectangle.height = transformToScreen(userData.getHeight());
}
protected float transformToScreen(float n) {
return Constants.WORLD_TO_SCREEN * n;
}
}
Runner Class
public class Runner extends GameActor {
...
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
float x = screenRectangle.x - (screenRectangle.width * 0.1f);
float y = screenRectangle.y;
float width = screenRectangle.width * 1.5f;
stateTime += Gdx.graphics.getRawDeltaTime();
batch.draw(runningAnimation.getKeyFrame(stateTime, true), x, y - 40, width, screenRectangle.height);
}
}
}
Trying to do a mouse event assignment. The idea is that when the user clicks the mouse the program draws a small green dot, when the mouse is released it should create a small orange dot 5 pixels down and 5 pixels right of it. The assigment requires the three programs, Dots, DotsPanel, and DrawDots. The program should also have a counter of total dots. Right now the programs compile, but the program completely breaks and gives me tons of errors when I try to run it. I'm a bit lost right now as I'm new to this GUI and user mouseEvents. Thank you! I specifically have trouble of how to get past the casting the Dots class as a Point.
Here is a copy of the instructions:
Modify the “Dots” example. Include a Dots class that contains a constructor to initialize the color and point location of the dot. The Dots class should also include two accessor methods. The ArrayList will now contain a list of the dots to draw instead of points. When the mouse is pressed, the program draws a green circle (as in the original design). When the mouse is released, the program draws a yellow circle translated 5 pixels right and 5 pixels down from the released location. Be sure to keep track of the total number of dots. You should have three files: Dots, DotsPanel, DrawDots. (10 points)
/**
* #(#)Dots.java
*
*
* #author
* #version 1.00 2015/5/16
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class Dots
{
Point p;
Color col;
public Dots(Color c, Point x)
{
p=x;
col=c;
}
public Point getPointP()
{
return p.getLocation();
}
public Color getPointC()
{
return col;
}
}
....
//represents the primary panel for the Dots program on which the
//dots are drawn
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class DotsPanel extends JPanel
{
private final int WIDTH = 300, HEIGHT = 300;
private final int RADIUS = 6;
private ArrayList<Dots> pointList;
private int count;
//Sets up this panel to listen for mouse events.
public DotsPanel()
{
pointList = new ArrayList<Dots>();
count = 0;
addMouseListener(new DotsListener());
setBackground(Color.black);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
}
//Draws all of the dots stored in the list
public void paintComponent(Graphics page)
{
super.paintComponent(page);
//Retrieve an iterator for the ArrayList of points
Iterator pointIterator = pointList.iterator();
while(pointIterator.hasNext())
{
page.setColor(Color.green);
Point drawPoint = (Point) pointIterator.next();
page.fillOval(drawPoint.x - RADIUS, drawPoint.y - RADIUS,
RADIUS * 2, RADIUS * 2);
page.setColor(Color.yellow);
Point drawPoint2 = (Point) pointIterator.next();
page.fillOval(drawPoint2.x - RADIUS+5, drawPoint2.y - RADIUS+5,
RADIUS * 2, RADIUS * 2);
}
page.drawString("Count: " + count, 5, 15);
}
//represents the lister for mouse events
private class DotsListener implements MouseListener
{
//Adds the current point to the list of points and redraws
//whenever the mouse button is pressed
public void mousePressed(MouseEvent event)
{
Dots dot1 = new Dots(Color.GREEN, event.getPoint());
pointList.add(dot1);
count++;
repaint();
}
public void mouseReleased(MouseEvent event)
{
Dots dot2 = new Dots(Color.YELLOW, event.getPoint());
pointList.add(dot2);
count++;
repaint();
}
//Provide empty definitions for unused event methods
public void mouseClicked(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
}
}
.....
//demonstrates mouse events and drawing on a panel
import javax.swing.*;
public class DrawDots
{
//Creates and displays the application frame
public static void main(String[] args)
{
JFrame dotsFrame = new JFrame("Dots");
dotsFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
dotsFrame.getContentPane().add(new DotsPanel());
dotsFrame.pack();
dotsFrame.setVisible(true);
}
}
First you should use Iterator<Dots> pointIterator = pointList.iterator();. This helps the compiler to warn you in case of uncastable types (this is the first error you got). In your paintComponent method you should use Point drawPoint = pointIterator.next().getPointP(); and Point drawPoint2 = pointIterator.next().getPointP(); a view lines later.After that modifications the program runs (at least on my machine).
I have these 3 classes which is supposed to work together to create a game. But I'm getting an error in one of them where it wants me to add unimplemented methods. The class which is creating the error is called Game and looks like this.
package org.game.main;
import java.awt.Graphics2D;
public class Game extends Window {
public static void main(String[] args) {
Window window = new Game();
window.run(1.0 / 60.0);
System.exit(0);
}
public Game() {
// call game constructor
super("Test Game", 640, 480);
}
public void gameStartup() {
}
public void gameUpdate(double delta) {
}
public void gameDraw(Graphics2D g) {
}
public void gameShutdown() {
}
}
It wants me to implement the method called Update() from the Window class. The Window class looks like this.
package org.game.main;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import org.game.input.*;
/**
* Game that creates a window and handles input.
* #author Eric
*/
public abstract class Window extends GameLoop {
private Frame frame;
private Canvas canvas;
private BufferStrategy buffer;
private Keyboard keyboard;
private Mouse mouse;
private MouseWheel mouseWheel;
/**
* Creates a new game window.
*
* #param title title of the window.
* #param width width of the window.
* #param height height of the window.
*/
public Window(String title, int width, int height) {
/*Log.debug("Game", "Creating game " +
title + " (" + width + ", " + height + ")");*/
// create frame and canvas
frame = new Frame(title);
frame.setResizable(false);
canvas = new Canvas();
canvas.setIgnoreRepaint(true);
frame.add(canvas);
// resize canvas and make the window visible
canvas.setSize(width, height);
frame.pack();
frame.setVisible(true);
// create buffer strategy
canvas.createBufferStrategy(2);
buffer = canvas.getBufferStrategy();
// create our input classess and add them to the canvas
keyboard = new Keyboard();
mouse = new Mouse();
mouseWheel = new MouseWheel();
canvas.addKeyListener(keyboard);
canvas.addMouseListener(mouse);
canvas.addMouseMotionListener(mouse);
canvas.addMouseWheelListener(mouseWheel);
canvas.requestFocus();
}
/**
* Get the width of the window.
*
* #return the width of the window.
*/
public int getWidth()
{
return canvas.getWidth();
}
/**
* Get the height of the window.
*
* #return the height of the window.
*/
public int getHeight()
{
return canvas.getHeight();
}
/**
* Returns the title of the window.
*
* #return the title of the window.
*/
public String getTitle()
{
return frame.getTitle();
}
/**
* Returns the keyboard input manager.
* #return the keyboard.
*/
public Keyboard getKeyboard()
{
return keyboard;
}
/**
* Returns the mouse input manager.
* #return the mouse.
*/
public Mouse getMouse()
{
return mouse;
}
/**
* Returns the mouse wheel input manager.
* #return the mouse wheel.
*/
public MouseWheel getMouseWheel() {
return mouseWheel;
}
/**
* Calls gameStartup()
*/
public void startup() {
gameStartup();
}
/**
* Updates the input classes then calls gameUpdate(double).
* #param delta time difference between the last two updates.
*/
public void update(double delta) {
// call the input updates first
keyboard.update();
mouse.update();
mouseWheel.update();
// call the abstract update
gameUpdate(delta);
}
/**
* Calls gameDraw(Graphics2D) using the current Graphics2D.
*/
public void draw() {
// get the current graphics object
Graphics2D g = (Graphics2D)buffer.getDrawGraphics();
// clear the window
g.setColor(Color.BLACK);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
// send the graphics object to gameDraw() for our main drawing
gameDraw(g);
// show our changes on the canvas
buffer.show();
// release the graphics resources
g.dispose();
}
/**
* Calls gameShutdown()
*/
public void shutdown() {
gameShutdown();
}
public abstract void gameStartup();
public abstract void gameUpdate(double delta);
public abstract void gameDraw(Graphics2D g);
public abstract void gameShutdown();
}
The last class is called Gameloop and looks like this.
package org.game.main;
public abstract class GameLoop {
private boolean runFlag = false;
/**
* Begin the game loop
* #param delta time between logic updates (in seconds)
*/
public void run (double delta) {
runFlag = true;
startup();
// convert the time to seconds
double nextTime = (double) System.nanoTime() / 1000000000.0;
double maxTimeDiff = 0.5;
int skippedFrames = 1;
int maxSkippedFrames = 5;
while (runFlag) {
// convert the time to seconds
double currTime = (double) System.nanoTime() / 1000000000.0;
if ((currTime - nextTime) > maxTimeDiff) nextTime = currTime;
if (currTime >= nextTime) {
// assign the time for the next update
nextTime += delta;
update();
if ((currTime < nextTime) || (skippedFrames > maxSkippedFrames)) {
draw();
skippedFrames = 1;
}
else {
skippedFrames++;
}
} else {
// calculate the time to sleep
int sleepTime = (int)(1000.0 * (nextTime - currTime));
// sanity check
if (sleepTime > 0) {
// sleep until the next update
try {
Thread.sleep(sleepTime);
}
catch(InterruptedException e) {
// do nothing
}
}
}
}
shutdown();
}
public void stop() {
runFlag = false;
}
public abstract void startup();
public abstract void shutdown();
public abstract void update();
public abstract void draw();
}
The error I'm getting in console when I run the main class looks like this.
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The type Game must implement the inherited abstract method GameLoop.update()
at org.game.main.Game.update(Game.java:5)
at org.game.main.GameLoop.run(GameLoop.java:26)
at org.game.main.Game.main(Game.java:8)
I hope you can help me. I'm quite new to java.
The signature of the update method is not overriding the interface, if that's what you intended.
public void update(double delta)
You need it to match the interface
public abstract void update();
So it sounds like this simple change should help:
public abstract void update(double delta);
In GameLoop you have defined
public abstract void update();
This method must be implemented in one of the sub classes Window or Game with the same signature.
You have implement unimplemented methods of Game Window and GameLoop class
Implement abstract methods. Window class' abstract methods are following:
public abstract void gameStartup();
public abstract void gameUpdate(double delta);
public abstract void gameDraw(Graphics2D g);
public abstract void gameShutdown();
Also implement following methods of GameLoop class
public abstract void startup();
public abstract void shutdown();
public abstract void update();
public abstract void draw();
Okay i found it out with some of the help that you suggested. It was because I hadn't defined Update() in GameLoop as.
public abstract void update(double delta);
Insted of
public abstract void update();
So the method didn't get called in Window. So then it had to be called in Game. Thanks for the help, everything works as should now.
This program is about creating components and moving them randomly in JApplet and removing the components if required. Every mouse click creates a new component. After creating component we can click on it and drag it where ever we want. Double clicking on the component removes it. I was successful in creating, moving and removing components.
For example i created three components on the JApplet by clicking three times randomly at different places on the JApplet.Please click here to view created components I moved them randomly around the screen. Please click here to view JApplet after randomly moving them around screen My goal is to write the final coordinates(x,y) of their position on the screen for all components created in to a text file by clicking a write button. I have no idea about mouseReleased method. Please help me with this. Thank you. Your help is very much appreciated.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.awt.geom.*;
import javax.swing.*;
This is my main class
public class MouseTest extends JApplet
{
public void init()
{
EventQueue.invokeLater(new Runnable() {
public void run()
{
MousePanel panel = new MousePanel();
add(panel);
}
});
}
public static void main(String[] args)
{
EventQueue.invokeLater(new mainThread());
}
}
class mainThread implements Runnable
{
public void run()
{
JPanel panel = new MousePanel();
JFrame frame = new JFrame("MouseTest");
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300,200);
frame.setVisible(true);
}
}
class MousePanel extends JPanel
{
public MousePanel()
{
squares = new ArrayList<Rectangle2D>();
current = null;
addMouseListener(new MouseHandler());
addMouseMotionListener(new MouseMotionHandler());
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// draw all squares
for (Rectangle2D r : squares)
g2.draw(r);
}
/**
* Finds the first square containing a point.
* #param p a point
* #return the first square that contains p
*/
public Rectangle2D find(Point2D p)
{
for (Rectangle2D r : squares)
{
if (r.contains(p)) return r;
}
return null;
}
/**
* Adds a square to the collection.
* #param p the center of the square
*/
public void add(Point2D p)
{
double x = p.getX();
double y = p.getY();
current = new Rectangle2D.Double(x - SIDELENGTH / 2, y - SIDELENGTH / 2, SIDELENGTH,
SIDELENGTH);
squares.add(current);
repaint();
}
/**
* Removes a square from the collection.
* #param s the square to remove
*/
public void remove(Rectangle2D s)
{
if (s == null) return;
if (s == current) current = null;
squares.remove(s);
repaint();
}
private static final int SIDELENGTH = 10;
private ArrayList<Rectangle2D> squares;
private Rectangle2D current;
// the square containing the mouse cursor
these are methods for defining and dragging and removing the object
private class MouseHandler extends MouseAdapter
{
public void mousePressed(MouseEvent event)
{
// add a new square if the cursor isn't inside a square
current = find(event.getPoint());
if (current == null) add(event.getPoint());
}
public void mouseClicked(MouseEvent event)
{
// remove the current square if double clicked
current = find(event.getPoint());
if (current != null && event.getClickCount() >= 2) remove(current);
}
}
private class MouseMotionHandler implements MouseMotionListener
{
public void mouseMoved(MouseEvent event)
{
// set the mouse cursor to cross hairs if it is inside
// a rectangle
if (find(event.getPoint()) == null) setCursor(Cursor.getDefaultCursor());
else setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
}
public void mouseDragged(MouseEvent event)
{
if (current != null)
{
int x = event.getX();
int y = event.getY();
// drag the current rectangle to center it at (x, y)
current.setFrame(x - SIDELENGTH / 2, y - SIDELENGTH / 2, SIDELENGTH, SIDELENGTH);
repaint();
}
}
}
}
Why do you keep changing the requirement???? See original question: How to find final coordinates(x & y) of the objects position in JApplet and write them to a text file
First you wanted to write out the location of the moved component after the drag was finished.
Then you wanted to write out the location of "all" components after the drag was finished.
Now you want to write out the location of all the components after a "button" is clicked.
I have no idea about mouseReleased method.
What does this have to do with anything? That was the suggestion for your requirement in your last question. Since your requirement has changed it is no longer needed.
My goal is to write the final coordinates(x,y) of their position on the screen for all components created in to a text file by clicking a write button.
Then you need to add an ActionListener to the button. You have already been given a link to the Swing tutorial on How to Write an ActionListener so I guess the question is what code should be included in the ActionListener?
In you last question you stated:
I know how to get the location of a component
I know how to write data to a file
So the only remaining problem is how to get all the components on the panel? To do this you can use the Container.getComponents() method on your panel containing all the dragged components. The basic code for your ActionListener would be something like:
for (Component component: panel.getComponents())
{
Point location = component.getLocation();
// write the location to the file
}
I'll let you add the code for opening/closing the file.
I have a class called ComponentMover that allows you to drag around any object that's registered with it. This works if you pass it bare JPanels, but it doesn't work if the JPanel has anything inside of it, and it just stays put.
This is ComponentMover:
public class ComponentMover extends MouseAdapter {
private Insets dragInsets = new Insets(0, 0, 0, 0);
private Dimension snapSize = new Dimension(1, 1);
private Insets edgeInsets = new Insets(0, 0, 0, 0);
private boolean changeCursor = true;
private boolean autoLayout = false;
private Class<?> destinationClass;
private Component destinationComponent;
private Component destination;
private Component source;
private Point pressed;
private Point location;
private Cursor originalCursor;
private boolean autoscrolls;
private boolean potentialDrag;
private boolean shouldLock = true;
/**
* Constructor for moving individual components. The components must be
* regisetered using the registerComponent() method.
*/
public ComponentMover() {
}
/**
* Constructor to specify a Class of Component that will be moved when drag
* events are generated on a registered child component. The events will be
* passed to the first ancestor of this specified class.
*
* #param destinationClass
* the Class of the ancestor component
* #param component
* the Components to be registered for forwarding drag events to
* the ancestor Component.
*/
public ComponentMover(Class<?> destinationClass, JComponent... components) {
this.destinationClass = destinationClass;
registerComponent(components);
}
/**
* Constructor to specify a parent component that will be moved when drag
* events are generated on a registered child component.
*
* #param destinationComponent
* the component drage events should be forwareded to
* #param components
* the Components to be registered for forwarding drag events to
* the parent component to be moved
*/
public ComponentMover(JComponent destinationComponent,
JComponent... components) {
this.destinationComponent = destinationComponent;
registerComponent(components);
}
public void setLock(boolean shouldLock) {
this.shouldLock = shouldLock;
}
/**
* Get the auto layout property
*
* #return the auto layout property
*/
public boolean isAutoLayout() {
return autoLayout;
}
/**
* Set the auto layout property
*
* #param autoLayout
* when true layout will be invoked on the parent container
*/
public void setAutoLayout(boolean autoLayout) {
this.autoLayout = autoLayout;
}
/**
* Get the change cursor property
*
* #return the change cursor property
*/
public boolean isChangeCursor() {
return changeCursor;
}
/**
* Set the change cursor property
*
* #param changeCursor
* when true the cursor will be changed to the Cursor.MOVE_CURSOR
* while the mouse is pressed
*/
public void setChangeCursor(boolean changeCursor) {
this.changeCursor = changeCursor;
}
/**
* Get the drag insets
*
* #return the drag insets
*/
public Insets getDragInsets() {
return dragInsets;
}
/**
* Set the drag insets. The insets specify an area where mouseDragged events
* should be ignored and therefore the component will not be moved. This
* will prevent these events from being confused with a MouseMotionListener
* that supports component resizing.
*
* #param dragInsets
*/
public void setDragInsets(Insets dragInsets) {
this.dragInsets = dragInsets;
}
/**
* Get the bounds insets
*
* #return the bounds insets
*/
public Insets getEdgeInsets() {
return edgeInsets;
}
/**
* Set the edge insets. The insets specify how close to each edge of the
* parent component that the child component can be moved. Positive values
* means the component must be contained within the parent. Negative values
* means the component can be moved outside the parent.
*
* #param edgeInsets
*/
public void setEdgeInsets(Insets edgeInsets) {
this.edgeInsets = edgeInsets;
}
/**
* Remove listeners from the specified component
*
* #param component
* the component the listeners are removed from
*/
public void deregisterComponent(JComponent... components) {
for (JComponent component : components)
component.removeMouseListener(this);
}
/**
* Add the required listeners to the specified component
*
* #param component
* the component the listeners are added to
*/
public void registerComponent(JComponent... components) {
for (JComponent component : components){
component.addMouseListener(this);
}
}
/**
* Get the snap size
*
* #return the snap size
*/
public Dimension getSnapSize() {
return snapSize;
}
/**
* Set the snap size. Forces the component to be snapped to the closest grid
* position. Snapping will occur when the mouse is dragged half way.
*/
public void setSnapSize(Dimension snapSize) {
if (snapSize.width < 1 || snapSize.height < 1)
throw new IllegalArgumentException(
"Snap sizes must be greater than 0");
this.snapSize = snapSize;
}
/**
* Setup the variables used to control the moving of the component:
*
* source - the source component of the mouse event destination - the
* component that will ultimately be moved pressed - the Point where the
* mouse was pressed in the destination component coordinates.
*/
#Override
public void mousePressed(MouseEvent e) {
source = e.getComponent();
int width = source.getSize().width - dragInsets.left - dragInsets.right;
int height = source.getSize().height - dragInsets.top
- dragInsets.bottom;
Rectangle r = new Rectangle(dragInsets.left, dragInsets.top, width,
height);
if (r.contains(e.getPoint()))
setupForDragging(e);
}
private void setupForDragging(MouseEvent e) {
source.addMouseMotionListener(this);
potentialDrag = true;
// Determine the component that will ultimately be moved
if (destinationComponent != null) {
destination = destinationComponent;
} else if (destinationClass == null) {
destination = source;
} else // forward events to destination component
{
destination = SwingUtilities.getAncestorOfClass(destinationClass,
source);
}
pressed = e.getLocationOnScreen();
location = destination.getLocation();
if (changeCursor) {
originalCursor = source.getCursor();
source.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
}
// Making sure autoscrolls is false will allow for smoother dragging of
// individual components
if (destination instanceof JComponent) {
JComponent jc = (JComponent) destination;
autoscrolls = jc.getAutoscrolls();
jc.setAutoscrolls(false);
}
}
/**
* Move the component to its new location. The dragged Point must be in the
* destination coordinates.
*/
#Override
public void mouseDragged(MouseEvent e) {
Point dragged = e.getLocationOnScreen();
int dragX = getDragDistance(dragged.x, pressed.x, snapSize.width);
int dragY = getDragDistance(dragged.y, pressed.y, snapSize.height);
int locationX = location.x + dragX;
int locationY = location.y + dragY;
// Mouse dragged events are not generated for every pixel the mouse
// is moved. Adjust the location to make sure we are still on a
// snap value.
if (shouldLock) {
while (locationX < edgeInsets.left)
locationX += snapSize.width;
while (locationY < edgeInsets.top)
locationY += snapSize.height;
Dimension d = getBoundingSize(destination);
while (locationX + destination.getSize().width + edgeInsets.right > d.width)
locationX -= snapSize.width;
while (locationY + destination.getSize().height + edgeInsets.bottom > d.height)
locationY -= snapSize.height;
}
// Adjustments are finished, move the component
destination.setLocation(locationX, locationY);
}
/*
* Determine how far the mouse has moved from where dragging started (Assume
* drag direction is down and right for positive drag distance)
*/
private int getDragDistance(int larger, int smaller, int snapSize) {
int halfway = snapSize / 2;
int drag = larger - smaller;
drag += (drag < 0) ? -halfway : halfway;
drag = (drag / snapSize) * snapSize;
return drag;
}
/*
* Get the bounds of the parent of the dragged component.
*/
private Dimension getBoundingSize(Component source) {
if (source instanceof Window) {
GraphicsEnvironment env = GraphicsEnvironment
.getLocalGraphicsEnvironment();
Rectangle bounds = env.getMaximumWindowBounds();
return new Dimension(bounds.width, bounds.height);
} else {
return source.getParent().getSize();
}
}
/**
* Restore the original state of the Component
*/
#Override
public void mouseReleased(MouseEvent e) {
if (!potentialDrag)
return;
source.removeMouseMotionListener(this);
potentialDrag = false;
if (changeCursor)
source.setCursor(originalCursor);
if (destination instanceof JComponent) {
((JComponent) destination).setAutoscrolls(autoscrolls);
}
// Layout the components on the parent container
if (autoLayout) {
if (destination instanceof JComponent) {
((JComponent) destination).revalidate();
} else {
destination.validate();
}
}
}
}
What should I do with this code (or the other GUI's building code) to make me able to drag and drop components with children?
You have an adaptation of a ComponentMover by #camickr. The original and your version work fine with components that have children. Perhaps the problem is elsewhere. Consider posting an MCVE that illustrates the problem.
Here is a simple demo:
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestMove extends JPanel{
public TestMove() {
setLayout(null);
JPanel panel = new JPanel();
panel.add(new JLabel("label"));
panel.add(new JButton("button"));
panel.setBorder(BorderFactory.createLineBorder(Color.GREEN));
panel.setBounds(50, 50, 200, 50);
add(panel);
ComponentMover cm = new ComponentMover();
cm.registerComponent(panel);
}
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
TestMove panel = new TestMove();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
});
}
}