How to drag free-form lines (paths) with AffineTransform?
I'm making a Paint-like application in Java and one of the requirements is being able to drag drawn free-form shapes to a new location on a JPanel used for drawing. I've been trying to get AffineTransform to do this for a day now and what it does at the moment is while the required line(which is stored as a Path2D) is selected and dragged it does move. However, once there is no more line selected, the line goes back to its original location. Also, when I select it again, it is immediately shown in that new location (if that makes any sense); this is probably to the coordinate system being translated, but I'm not sure...
Any help will be highly appreciated! Perhaps there is an simpler way of doing this.
PS I also noticed that when moving any of the drawn lines, except for the one drawn last, all of the lines drawn prior to the line, which is being moved, are moved together with it.
Here's the code:
public class DrawPanel extends JPanel {
public double translateX=0;
public double translateY=0;
public int lastOffsetX;
public int lastOffsetY;
class Line {
public Point start;
public Point end;
public Color color;
public Path2D path;
}
ArrayList<Line> lines = new ArrayList<Line>();
ArrayList<Path2D> paths = new ArrayList<Path2D>();
boolean moveMode = false;
Path2D selectedLine;
int xDistance;
int yDistance;
public DrawPanel() {
setBackground(Color.WHITE);
setFocusable(true);
requestFocusInWindow();
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
//System.out.println(resizeMode);
if (moveMode) {
if (selectedLine!=null) {
int newX = e.getX() - lastOffsetX;
int newY = e.getY() - lastOffsetY;
lastOffsetX += newX;
lastOffsetY += newY;
translateX += newX;
translateY += newY;
repaint();
}
} else {
Path2D p = paths.get(paths.size() - 1);
p.lineTo(e.getX(), e.getY());
repaint();
}
}
#Override
public void mouseMoved(MouseEvent e) {
super.mouseMoved(e);
if (resizeMode) {
selectedLine = null;
for (Path2D l : paths) {
if (l.contains(e.getPoint())) {
selectedLine = l;
}
}
repaint();
}
}
});
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
if (!moveMode) {
Line l = new Line(e.getPoint());
l.path = new Path2D.Double();
l.path.moveTo(e.getX(), e.getY());
lines.add(l);
paths.add(l.path);
} else {
lastOffsetX = e.getX();
lastOffsetY = e.getY();
}
}
}
#Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
if (!resizeMode) {
if (selectedLine == null) {
Line l = lines.get(lines.size() - 1);
l.end = e.getPoint();
l.path.lineTo(e.getX(), e.getY());
Path2D p = paths.get(paths.size() - 1);
p.lineTo(e.getX(), e.getY());
repaint();
}
} else {
for (int j=0; j<paths.size();j++) {
if (selectedLine!=null && selectedLine.equals(paths.get(j))) {
paths.set(j, selectedLine);
}
}
repaint();
}
}
});
}
private void setKeyBindings() {
ActionMap actionMap = getActionMap();
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition );
String ctrl = "VK_CONTROL";
String ctrl_rel = "control_released";
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, KeyEvent.CTRL_DOWN_MASK, false), ctrl);
inputMap.put(KeyStroke.getKeyStroke(("released CONTROL")), ctrl_rel);
actionMap.put(ctrl, new KeyAction(ctrl));
actionMap.put(ctrl_rel, new KeyAction(ctrl_rel));
}
private class KeyAction extends AbstractAction {
public KeyAction(String actionCommand) {
putValue(ACTION_COMMAND_KEY, actionCommand);
}
#Override
public void actionPerformed(ActionEvent actionEvt) {
if(actionEvt.getActionCommand() == "VK_CONTROL") {
moveMode = true;
}
else if(actionEvt.getActionCommand() == "control_released") {
moveMode = false;
repaint();
}
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke(10.0f));
g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
if (moveMode) {
for (int j=0; j<paths.size();j++) {
Path2D path = paths.get(j);
if (selectedLine!=null && selectedLine.equals(path)) {
AffineTransform at = new AffineTransform();
at.translate(translateX, translateY);
g2d.transform(at);
g2d.setColor(Color.RED);
g2d.draw(path);
g2d.setColor(Color.BLACK);
continue;
}
g2d.draw(path);
g2d.setColor(Color.BLACK);
}
} else {
for (int i =0; i < paths.size();i++) {
Path2D path = paths.get(i);
g2d.draw(path); // outline
}
}
}
Edit to include resolution: So in the end what I did is I saved all of the path's coordinates (which I got from PathIterator) to and ArrayList, created a new empty path, added the previous path's coordinates from the ArrayList to the new path (via moveTo, lineTo) and appended the new path to the ArrayList of all drawn paths.
As you suspect, your AffineTransform alters the graphics context's coordinate system for all subsequent drawing.
In the example cited here, each shape is an instance of the class Node. Each Node includes a selected attribute, which allows the shape to be selected independently. The value determines the effect of updatePosition() when called from mouseDragged(). The implementation of updatePosition() simply updates each selected node's coordinates, but you can also use the createTransformedShape() of AffineTransform.
Related
As the title states, I am trying to detect a mouse hover over an object that is not a JComponent.
Right now I have a window with a green JPanel. When you left-click on this JPanel you create a point.
What I am trying to do is to have extra information displayed when I hover over these points. However, I have no idea how to even begin detecting if I am hovering my mouse over a point. I tried looking into the MouseListener interface but I could not find any examples of people using MouseListener with an object. I have only seen people use MouseListener with JComponents. I would preferably like to have this mouse hover detection code in my Point class if possible to keep my code clean.
JPanel Code
class Map extends JPanel implements MouseListener {
public static ArrayList<Point> points = new ArrayList<Point>(); //array for the points
public Map() {
this.setBackground(Color.green);
this.setPreferredSize(new Dimension(1280, 720));
this.addMouseListener(this);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g;
drawPoints(graphics);
}
private void drawPoints(Graphics2D graphics) {
for(int i = 0; i < points.size(); i++) {
points.get(i).drawPoint(graphics);
}
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1) { //Left Click
points.add(new Point(e.getX(), e.getY()));
repaint();
}
else if(e.getButton() == MouseEvent.BUTTON3) { //right click
for(int i = points.size() - 1; i >= 0; i--) { //loop backwards so if points overlap remove the one on top first
Point current = points.get(i);
if( Math.abs( e.getX() - current.x ) < current.size/2 && Math.abs( e.getY() - current.y ) < current.size/2 ) {
points.remove(i);
repaint();
break;
}
}
}
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
Point Code
public class Point {
public int x, y;
public int size = 10;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point() {
this.x = 0;
this.y = 0;
}
public void drawPoint(Graphics2D graphics) {
graphics.setPaint(Color.black);
graphics.setStroke(new BasicStroke(5));
graphics.drawOval(x - (size/2), y - (size/2), size, size);
graphics.setPaint(Color.red);
graphics.fillOval(x - (size/2), y - (size/2), size, size);
}
public void drawInfo(Graphics2D graphics) {
graphics.drawString("test", x, y);
}
}
I had the same issue with a Packet-Tracer-Like program, where I drew rects.
If they are just points, I would check if the mouse cords are the same as the point cords when the mouse is moved.
#Override
public void mouseMoved(MouseEvent e) {
entered = false;
if(point.x == e.getX() && point.y == e.getY()){
entered = true;
}
}
If although, like in my case, the drawn object has a width and a height, it gets messier.
#Override
public void mouseMoved(MouseEvent e) {
entered = false;
if((e.getX() <= point.x+width) && (e.getX() >= point.x)){
if((e.getY() <= point.y+height) && (e.getY() >= point.y)){
entered = true;
}
}
}
So I have two classes here:
PhotoComponent class:
(This class is to handle a specific image as a JComponent. When "flipped" I want to draw pen strokes instead of having an image. So I replace the image with a rectangle, attempting to draw pen strokes over it.)
public class PhotoComponent extends JComponent {
private Image pic;
private boolean flipped;
private int contentAreaWidth;
private int contentAreaHeight;
p
#Override
public void paintComponent(Graphics g) {
//Does all the drawing and contains whatever state information is associated with the photo
//create an action event to auto call repaint
//call repaint anytime flip was changed to true or false
System.out.println("Draw: " + draw + ", Pic: " + pic);
if (draw && pic != null) {
super.paintComponent(g);
System.out.println("width using this: " + this.getWidth() + ", actual width of JPanel: " + contentAreaWidth);
System.out.println("height using this: " + this.getHeight() + ", actual height of JPanel: " + contentAreaHeight);
g2 = (Graphics2D) g;
int x = (contentAreaWidth - pic.getWidth(null)) / 2;
int y = (contentAreaHeight - pic.getHeight(null)) / 2;
if (!flipped) {
g2.drawImage(pic, x, y, null);
} else if (flipped) {
g2.setColor(Color.WHITE);
g2.fillRect(x,y,pic.getWidth(null), pic.getHeight(null));
g2.drawRect(x, y, pic.getWidth(null), pic.getHeight(null));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (drawingMode) {
g2.setPaint(Color.RED);
if (drawOval) {
penStrokes.put(ovalX, ovalY);
if (penStrokes != null) {
for (Integer xCoor : penStrokes.keySet()) {
g2.setPaint(Color.RED);
int brushSize = 5;
g2.fillOval((xCoor - (brushSize / 2)), (penStrokes.get(xCoor) - (brushSize / 2)), brushSize, brushSize);
//System.out.println("SIZE OF HASHTABLE: " + penStrokes.size());
}
}
System.out.println("Filling an oval!" + ovalX + ", " + ovalY);
}
} else if (textMode) {
g2.setPaint(Color.YELLOW);
if (drawRect) {
rectDimensions.add(rectX);
rectDimensions.add(rectY);
rectDimensions.add(rectWidth);
rectDimensions.add(rectHeight);
for (int i = 0; i < rectDimensions.size(); i+=4) {
g2.fillRect(rectDimensions.get(i), rectDimensions.get(i+1), rectDimensions.get(i+2), rectDimensions.get(i+3));
g2.drawRect(rectDimensions.get(i), rectDimensions.get(i+1), rectDimensions.get(i+2), rectDimensions.get(i+3));
}
}
}
System.out.println("This is being called again!");
}
}
}
public void setRectangle(int x, int y, int width, int height) {
drawRect = true;
rectX = x;
rectY = y;
rectWidth = width;
rectHeight = height;
}
public void removeRectangle() {
drawRect = false;
}
public int[] setOval(int currentX, int currentY) {
drawOval = true;
int[] toReturn = {ovalX, ovalY};
ovalX =
NOTE THE DRAWLINE() METHOD ABOVE. I am drawing at the given points, repainting, and setting the old variables to be the current variables.
Main class:
private static PhotoComponent img;
private static JFrame frame;
private static JPanel contentArea;
//Mouse Coordinates for PenStrokes
private static int oldX, oldY;
//Mouse Coordinates for Sticky Notes
private static Point clickPoint;
public static void main (String[] args) {
frame = new JFrame("PhotoManip");
img = null;
contentArea = null;
oldX = 0;
oldY = 0;
setupMenubar(frame);
setupJFrame(frame);
}
private static void addPhotoComponent(File file) {
}
if (img.getTextMode()) {
img.removeRectangle();
clickPoint = null;
}
}
});
img.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
if (img.getDrawingMode()) {
if (withinRange(e.getX(), e.getY())) {
int[] toUpdate = img.setOval(e.getX(), e.getY());
oldX = toUpdate[0];
oldY = toUpdate[1];
img.repaint();
}
}
if (img.getTextMode()) {
if (withinRange(e.getX(), e.getY())) {
Point dragPoint = e.getPoint();
h, height);
img.repaint();
}
}
}
});
if (img!=null) {
contentArea.add(img);
}
}
private static boolean withinRange(int x, int y) {
if (x > img.getX() && x < img.getX() + img.getWidth()) {
if (y > img.getY() && y < img.getY() + img.getHeight()) {
return true;
}
}
return false;
}
private static void flipImage() {
if (!img.isFlipped()) {
img.setFlipped(true);
} else if (img.isFlipped()) {
img.setFlipped(false);
}
}
drawLine() is called above in this main class, when a mousedrag occurs. Problem is that the strokes don't appear to show.
I know that the program is calling g2.fillOval() because I am printing out a verification statement afterwards.
Additionally, I have created print statements for when the mouse is pressed and dragged and they are getting the correct coordinates?
Why don't red strokes appear? I'm confused. Is it the way my code is structured?
The crux of your problem is that you are trying to draw something outside the paintComponent method, which is never supported. Whatever you draw will get overwritten by the next call of paintComponent, which will happen almost instantly. We can solve this by storing the co-ordinates of the oval and drawing it within paintComponent instead of trying to draw on a graphics object outside of the paintComponent method. See code below:
First we are going to add the following variables to your PhotoComponent class:
private boolean drawOval = false;
private int ovalX = 0;
private int ovalY = 0;
Then we will add methods for controlling them:
public int[] setOval(int currentX, int currentY) {
drawOval = true;
int[] toReturn = {ovalX, ovalY};
ovalX = currentX;
ovalY = currentY;
return toReturn;
}
public void removeOval() {
drawOval = false;
}
After that we can change the paintComponent method to have it draw the oval based on those variables:
#Override
public void paintComponent(Graphics g) {
//Does all the drawing and contains whatever state information is associated with the photo
//create an action event to auto call repaint
//call repaint anytime flip was changed to true or false
super.paintComponent(g);
g2 = (Graphics2D) g;
int x = (contentAreaWidth - pic.getWidth(null)) / 2;
int y = (contentAreaHeight - pic.getHeight(null)) / 2;
if (!flipped) {
g2.drawImage(pic, x, y, null);
} else if (flipped) {
g2.setColor(Color.WHITE);
g2.fillRect(x, y, pic.getWidth(null), pic.getHeight(null));
g2.drawRect(x, y, pic.getWidth(null), pic.getHeight(null));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(Color.RED);
}
//took the code you already used for drawing the oval and moved it here
if (drawOval) {
g2.setPaint(Color.RED);
int brushSize = 5;
g2.fillOval((ovalX - (brushSize / 2)), (ovalY - (brushSize / 2)), brushSize, brushSize);
}
}
Finally change the addPhotoComponent method to update those variables instead of trying to draw the oval directly:
private static void addPhotoComponent(File file) {
Image image = null;
try {
image = ImageIO.read(file);
} catch (IOException e2) {
System.out.println("ERROR: Couldn't get image.");
}
img = new PhotoComponent(image, contentArea);
img.revalidate();
img.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
// your code here
System.out.println("You flipped the photo!!!");
flipImage();
img.repaint();
}
}
#Override
public void mousePressed(MouseEvent e) {
img.setOval(e.getX(), e.getY());
}
#Override
public void mouseReleased(MouseEvent e) {
img.removeOval();
}
});
img.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
int[] toUpdate = img.setOval(e.getX(), e.getY());
oldX = toUpdate[0];
oldY = toUpdate[1];
}
});
if (img != null) {
contentArea.add(img);
}
}
I can not resize the two rectangles, but only the right one. What should I add to my code ?
I want also that when the lower edge of the left rectangle is moved, it also moves the upper edge of the right rectangle.
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class Resizing extends JPanel {
Rectangle rect = new Rectangle(100,100,150,150);
Rectangle rect2 = new Rectangle(300,100,150,150);
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(new Color(0, 0, 200));
g2.fill(rect);
g2.setColor(new Color(0, 0, 200));
g2.fill(rect2);
}
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Resizing essai = new Resizing();
Resizer1 rectangle = new Resizer1(essai);
essai.addMouseListener(rectangle);
essai.addMouseMotionListener(rectangle);
f.add(essai);
Resizing test2 = new Resizing();
Resizer2 rectangle2 = new Resizer2(test2);
test2.addMouseListener(rectangle2);
test2.addMouseMotionListener(rectangle2);
f.add(test2);
f.setSize(600,400);
f.setLocation(100,100);
f.setVisible(true);
}
}
class Resizer1 extends MouseAdapter {
Resizing component;
boolean dragging = false;
// Give user some leeway for selections.
final int PROX_DIST = 3;
public Resizer1(Resizing r) {
component = r;
}
#Override
public void mousePressed(MouseEvent e) {
if(component.getCursor() != Cursor.getDefaultCursor()) {
// If cursor is set for resizing, allow dragging.
dragging = true;
}
}
#Override
public void mouseReleased(MouseEvent e) {
dragging = false;
}
#Override
public void mouseDragged(MouseEvent e) {
if(dragging){
Point p = e.getPoint();
Rectangle r = component.rect;
int type = component.getCursor().getType();
int dy = p.y - r.y;
switch(type) {
case Cursor.N_RESIZE_CURSOR:
int height = r.height - dy;
r.setRect(r.x, r.y+dy, r.width, height);
break;
case Cursor.S_RESIZE_CURSOR:
height = dy;
r.setRect(r.x, r.y, r.width, height);
break;
default:
System.out.println("unexpected type: " + type);
}
component.repaint();
}
}
#Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
if(!isOverRect(p)) {
if(component.getCursor() != Cursor.getDefaultCursor()) {
// If cursor is not over rect reset it to the default.
component.setCursor(Cursor.getDefaultCursor());
}
return;
}
// Locate cursor relative to center of rect.
int outcode = getOutcode(p);
Rectangle r = component.rect;
switch(outcode) {
case Rectangle.OUT_TOP:
if(Math.abs(p.y - r.y) < PROX_DIST) {
component.setCursor(Cursor.getPredefinedCursor(
Cursor.N_RESIZE_CURSOR));
}
break;
case Rectangle.OUT_BOTTOM:
if(Math.abs(p.y - (r.y+r.height)) < PROX_DIST) {
component.setCursor(Cursor.getPredefinedCursor(
Cursor.S_RESIZE_CURSOR));
}
break;
default: // center
component.setCursor(Cursor.getDefaultCursor());
}
}
/**
* Make a smaller Rectangle and use it to locate the
* cursor relative to the Rectangle center.
*/
private int getOutcode(Point p) {
Rectangle r = (Rectangle)component.rect.clone();
r.grow(-PROX_DIST, -PROX_DIST);
return r.outcode(p.x, p.y);
}
/**
* Make a larger Rectangle and check to see if the
* cursor is over it.
*/
private boolean isOverRect(Point p) {
Rectangle r = (Rectangle)component.rect.clone();
r.grow(PROX_DIST, PROX_DIST);
return r.contains(p);
}
}
class Resizer2 extends MouseAdapter {
Resizing component;
boolean dragging = false;
// Give user some leeway for selections.
final int PROX_DIST = 3;
public Resizer2(Resizing r) {
component = r;
}
#Override
public void mousePressed(MouseEvent e2) {
if(component.getCursor() != Cursor.getDefaultCursor()) {
// If cursor is set for resizing, allow dragging.
dragging = true;
}
}
#Override
public void mouseReleased(MouseEvent e2) {
dragging = false;
}
#Override
public void mouseDragged(MouseEvent e2) {
if(dragging){
Point p = e2.getPoint();
Rectangle r = component.rect2;
int type = component.getCursor().getType();
int dy = p.y - r.y;
switch(type) {
case Cursor.N_RESIZE_CURSOR:
int height = r.height - dy;
r.setRect(r.x, r.y+dy, r.width, height);
break;
case Cursor.S_RESIZE_CURSOR:
height = dy;
r.setRect(r.x, r.y, r.width, height);
break;
default:
System.out.println("unexpected type: " + type);
}
component.repaint();
}
}
#Override
public void mouseMoved(MouseEvent e2) {
Point p = e2.getPoint();
if(!isOverRect(p)) {
if(component.getCursor() != Cursor.getDefaultCursor()) {
// If cursor is not over rect reset it to the default.
component.setCursor(Cursor.getDefaultCursor());
}
return;
}
// Locate cursor relative to center of rect.
int outcode = getOutcode(p);
Rectangle r = component.rect2;
switch(outcode) {
case Rectangle.OUT_TOP:
if(Math.abs(p.y - r.y) < PROX_DIST) {
component.setCursor(Cursor.getPredefinedCursor(
Cursor.N_RESIZE_CURSOR));
}
break;
case Rectangle.OUT_BOTTOM:
if(Math.abs(p.y - (r.y+r.height)) < PROX_DIST) {
component.setCursor(Cursor.getPredefinedCursor(
Cursor.S_RESIZE_CURSOR));
}
break;
default: // center
component.setCursor(Cursor.getDefaultCursor());
}
}
/**
* Make a smaller Rectangle and use it to locate the
* cursor relative to the Rectangle center.
*/
private int getOutcode(Point p) {
Rectangle r = (Rectangle)component.rect2.clone();
r.grow(-PROX_DIST, -PROX_DIST);
return r.outcode(p.x, p.y);
}
/**
* Make a larger Rectangle and check to see if the
* cursor is over it.
*/
private boolean isOverRect(Point p) {
Rectangle r = (Rectangle)component.rect2.clone();
r.grow(PROX_DIST, PROX_DIST);
return r.contains(p);
}
}
Problems I see on inspection of your code:
You are adding 2 JPanels to your JFrame, but only one will show since they're being added in a default fashion to a BorderLayout-using container. Edit: I see now why you're doing this, but as explained below, you should not be doing this. Only create one Resizing object and add it once to the JFrame. The one Resizing will show both rectangles, and the single MouseAdapter should be coded to allow you to interact with both rectangles.
You are not making your Rectangle fields private and are allowing outside classes (namely Resizer1) to directly access and minipulate your fields. You'd be better off using public methods that allow other classes to selectively query your class for its state or to change your class's state.
Don't use two Resizer classes, Resizer1 and Resizer2, and it is this as well as your adding two Resizing objects to the JFrame that are in fact the main reason for your problems. Instead, use just one Resizer class, and use it as a single MouseAdapter added to a single Resizing object. Then in this single class, allow both rectangles to be changed.
Please post comments if you have any questions.
You ask:
Thank you, but how can I allow both rectangles to be changed ? In class Resizer, there is only one component (r) ?
There is only one component, but it holds two rectangles, and it knows the difference between the two rectangles since it has two rectangle variables.
Edit 2
Consider:
Editing your original question and adding your new code to the bottom, deleting your redundant new question.
Creating a non-GUI object, say called MyRectangle, that holds a Rectangle object.
This new class can have methods that allow your to pass in a Point or an x and y positions, and return information to let the calling code know if the mouse is over the top or bottom edge (your code already does this, so this should be no problem for you).
This new class will have mutator (setter) methods that allow outside classes set its rectangle y position and height.
The new class will have a fill method that accepts a Graphics2D parameter and uses it to fill the Rectangle that it holds.
Then give your Resizer class a List<MyRectangle>, actually an ArrayList of them, say called myRectangleList, and you can add two MyRectangle objects to it
Then give Resizer a getMyRectangleList method that returns the list.
Then in your MouseAdapter, iterate through the List to see if the mouse is over an edge
etc...
e.g.,
class MyRectangle {
private Rectangle rect;
private String name;
public MyRectangle(int x, int y, int width, int height, String name) {
rect = new Rectangle(x, y, width, height);
this.name = name;
}
public void fill(Graphics2D g2) {
g2.fill(rect);
}
public int getOutcode(Point p) {
// return ... do what you need to figure tihs out
}
public boolean isOverRect(Point p) {
// return ... do what you need to figure tihs out
}
public String getName() {
return name;
}
#Override
public String toString() {
return name + " " + rect.toString();
}
}
And then something like:
public class Resizing2 extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private static final Color RECT_COLOR = Color.blue;
private List<MyRectangle> rectangleList = new ArrayList<>();
public Resizing2() {
rectangleList.add(new MyRectangle(100, 100, 150, 150, "Rect 1"));
rectangleList.add(new MyRectangle(300, 100, 150, 150, "Rect 2"));
}
public List<MyRectangle> getRectangleList() {
return rectangleList;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(RECT_COLOR);
for (MyRectangle rect : rectangleList) {
rect.fill(g2);
}
}
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
// ..... etc...
My question has been alluded to in java draw line as the mouse is moved, however, I have not advanced far enough into this book to have covered JPanels, JFrames and Points as stated by the prior programmer who asked this question.
Answering this question definitely would help most beginner programmers better understand the graphics class and drawing, an often intricate process, especially for beginners.
According to the text I am using (as I am learning Java on my own), this was the example of how to draw a line using Java:
/*
* LineTest
* Demonstrates drawing lines
*/
import java.awt.*;
public class LineTest extends Canvas {
public LineTest() {
super();
setSize(300, 200);
setBackground(Color.white);
}
public static void main(String args[]) {
LineTest lt = new LineTest();
GUIFrame frame = new GUIFrame("Line Test");
frame.add(lt);
frame.pack();
frame.setVisible(true);
}
public void paint(Graphics g) {
g.drawLine(10, 10, 50, 100);
g.setColor(Color.blue);
g.drawLine(60, 110, 275, 50);
g.setColor(Color.red);
g.drawLine(50, 50, 300, 200);
}
}
The specification is:
Create an application that allows you to draw lines by clicking the initial
point and dragging the mouse to the second point. The application should
be repainted so that you can see the line changing size and position as you
are dragging the mouse. When the mouse button is released, the line is
drawn.
As you will recognize, running this program does not create any drawing by the user. I believe this error is encountered due to line 21: g.drawLine(x, y, x2, y2); being incorrect since this is the statement defining the drawing of the line.
Any help is greatly appreciated. Thank you in advance for all your time and cooperation regarding this matter.
My code to answer the question is:
import java.awt.*;
import java.awt.event.*;
public class LineDrawer extends Canvas
implements MouseListener, MouseMotionListener {
int x, y, x2, y2;
public LineDrawer() {
super();
setSize(300, 200);
setBackground(Color.white);
}
public void mouseClicked(MouseEvent me) {
int x = me.getX();
int y = me.getY();
int x2 = me.getX();
int y2 = me.getY();
}
public void paint(Graphics g) {
g.drawLine(x, y, x2, y2);
g.setColor(Color.blue);
}
public void mousePressed(MouseEvent me) {
repaint();
}
public void mouseDragged(MouseEvent me) {
}
public void mouseExited(MouseEvent me) {
}
public void mouseEntered(MouseEvent me) {
}
public void mouseReleased(MouseEvent me) {
}
public void mouseMoved(MouseEvent me) {
}
public static void main(String args[]) {
LineDrawer ld = new LineDrawer();
GUIFrame frame = new GUIFrame("Line Drawer");
frame.add(ld);
frame.pack();
frame.setVisible(true);
}
}
P.S.: I have been hesitant to ask for help since I am concerned that other programmers would answer with methods that I have not yet learned.
int x1, y1, x2, y2;
public void mousePressed(MouseEvent e){
x1 = e.getX();
y1 = e.getY();
}
public void mouseDragged(MouseEvent e){
x2 = e.getX();
y2 = e.getY();
// Now Paint the line
repaint();
}
Hope it helps.
Let's start with
public void mouseClicked(MouseEvent me) {
int x = me.getX();
int y = me.getY();
int x2 = me.getX();
int y2 = me.getY();
}
You have previous declared x, y, x2 and y2, but in this method, you have overridden those decelerations with new ones, meaning that the previous declared variables will not be used and the event parameters will be ignored.
mouseClicked is fired AFTER a mousePressed and mouseReleased event, meaning that this is actually when the user has released the mouse button.
Extreme Coder has pointed out that MouseClicked is only fired when the mouse button is pressed and released at the same point, i.e. no dragging is involved - it's still not the right method to use, but the clarification is nice
What you should do is...
On mousePressed store the x, y position of the click and on mouseReleased store the x2, y2 position.
On the mouseDragged event, you should update the x2, y2 values and call repaint
public void mousePressed(MouseEvent me) {
// Mouse is down, but hasn't yet being released...
x = me.getX();
y = me.getY();
// We need to "override" any previous values...
x2 = x;
y2 = y;
repaint();
}
public void mouseDragged(MouseEvent me) {
x2 = me.getX();
y2 = me.getY();
repaint();
}
public void mouseReleased(MouseEvent me) {
// Here I would store the points so I could re-draw each new line...
}
Instead of using x, y, x2 and y2, it might be better to use two arrays, ie
private int[] startPoint;
private int[] endPoint;
Then you could do something like...
public void mousePressed(MouseEvent me) {
// Mouse is down, but hasn't yet being released...
startPoint = new int[2];
startPoint[0] = me.getX();
startPoint[1] = me.getY();
endPoint = startPoint;
repaint();
}
public void mouseDragged(MouseEvent me) {
endPoint = new int[2];
endPoint[0] = me.getX();
endPoint[1] = me.getY();
repaint();
}
Now I prefer paintComponent from JComponent, but I'll stick to you example for now..
public void paint(Graphics g) {
super.paint(g); // This is super important...
if (startPoint != null && endPoint != null && startPoint.length == 2 && endPoint.length == 2) {
g.drawLine(startPoint[0], startPoint[1], endPoint[0], endPoint[1]);
}
}
Additional
This is of some concern...
public void paint(Graphics g) {
g.drawLine(x, y, x2, y2);
g.setColor(Color.blue);
}
The order of operation is VERY important. Setting the color AFTER you've painted the line with have no effect on you paint operations (but may effect paint operations that occur after you).
Also, you MUST call super.paint(g) - this is super important...
Examples
A "basic" example, using int[] arrays for point storage...
public class BasicLineDraw {
public static void main(String[] args) {
new BasicLineDraw();
}
public BasicLineDraw() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new DrawLinePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawLinePane extends JPanel {
private int[] startPoint;
private int[] endPoint;
private List<int[][]> lines;
public DrawLinePane() {
lines = new ArrayList<int[][]>(25);
MouseAdapter handler = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startPoint = new int[]{e.getX(), e.getY()};
endPoint = startPoint;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
endPoint = new int[]{e.getX(), e.getY()};
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (startPoint != null && endPoint != null && startPoint.length == 2 && endPoint.length == 2) {
lines.add(new int[][]{startPoint, endPoint});
}
startPoint = null;
endPoint = null;
repaint();
}
};
addMouseListener(handler);
addMouseMotionListener(handler);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (startPoint != null && endPoint != null && startPoint.length == 2 && endPoint.length == 2) {
g2d.setColor(Color.RED);
g2d.drawLine(startPoint[0], startPoint[1], endPoint[0], endPoint[1]);
}
g2d.setColor(Color.BLUE);
for (int[][] line : lines) {
g2d.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]);
}
g2d.dispose();
}
}
}
And a more advanced example, using Point and Java's 2D Graphics API
public class LineDrawer {
public static void main(String[] args) {
new LineDrawer();
}
public LineDrawer() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new DrawLinePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawLinePane extends JPanel {
private Point anchor;
private Point lead;
private List<Line2D> lines;
public DrawLinePane() {
lines = new ArrayList<Line2D>(25);
MouseAdapter handler = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
lead = null;
anchor = e.getPoint();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
lead = e.getPoint();
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (lead != null && anchor != null && !anchor.equals(lead)) {
lines.add(new Line2D.Float(anchor, lead));
}
repaint();
}
};
addMouseListener(handler);
addMouseMotionListener(handler);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
if (lead != null && anchor != null) {
Composite composite = g2d.getComposite();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.25f));
g2d.draw(new Line2D.Float(anchor, lead));
g2d.setComposite(composite);
}
for (Line2D line : lines) {
g2d.draw(line);
}
g2d.dispose();
}
}
}
I want to creat a circle in on a panel which appears and dissapears every 2 seconds.
Here is that i have:
public class Board extends JPanel implements ActionListener {
private final int DELAY = 2000;
private Timer timer;
/*
* constructor
*/
public Board() {
setFocusable(true);
initGame();
}
/*
* initialize board
*/
public void initGame() {
timer = new Timer(DELAY, this);
timer.start();
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.gray);
// draw an oval starting at 20,20 with a width and height of 100 and
// fill it
g.drawOval(20, 20, 100, 100);
g.fillOval(20, 20, 100, 100);
g.dispose();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
}
thank you guys. Now I wanna control my circle. But again something is wrong and it is not moving as I want.
here are new methods:
private boolean left = false;
private boolean right = true;
private boolean up = false;
private boolean down = false;
private Timer timer;
public int x = 20;
public int y = 20;
public int x2 = 100;
public int y2 = 100;
...
public void paint(Graphics g) {
super.paint(g);
if (drawCircle) {
g.setColor(Color.gray);
// draw an oval starting at 20,20 with a width and height of 100 and
// fill it
g.drawOval(x, y, x2, y2);
g.fillOval(x, y, x2, y2);
}
// removes native non-Java recourses
g.dispose();
}
public void move() {
if (left) {
x -= 5;
}
if (right) {
x += 5;
}
if (up) {
y -= 5;
}
if (down) {
y += 5;
}
}
#Override
public void actionPerformed(ActionEvent e) {
move();
repaint();
}
private class TAdapter extends KeyAdapter {
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if ((key == KeyEvent.VK_LEFT) && (!right)) {
left = true;
up = false;
down = false;
}
if ((key == KeyEvent.VK_RIGHT) && (!left)) {
right = true;
up = false;
down = false;
}
if ((key == KeyEvent.VK_UP) && (!down)) {
up = true;
right = false;
left = false;
}
if ((key == KeyEvent.VK_DOWN) && (!up)) {
down = true;
right = false;
left = false;
}
}
}
/****/
Solved , i just added
addKeyListener(new TAdapter());
to my Board constructor!
You just need to have your Timer modify some state. Try something like this:
private boolean drawCircle = false;
public void actionPerformed(ActionEvent e) {
drawCircle = !drawCircle;
repaint();
}
public void paintComponent(Graphics g) {
//...
if ( drawCircle ) {
g.setColor(Color.gray);
//...
}
}
Aren't you supposed to formulate the question... as a question?
Anyway, add a boolean to your class, toggle it on each action event, and paint the oval only if it is true.
Edit/Notes:
- Override paintComponent instead of paint
- Don't dispose of a Graphics you haven't created. Either drop the line, or use g.create() to make a copy. See http://java.sun.com/products/jfc/tsc/articles/swing2d/ for details.