JPanel subclass "jumps around" when dragged - java

I'm currently coding on a small "Paint"-program of mine; so far, you can draw on it with a pen, zoom in up to 100x, and choose a color. Next thing I wanted to add was (or is) the possibility to drag the JPanel subclass, on which the image chosen to edit is drawn, around. Basically, by holding down the right mouse button, you change the location of the JPanel subclass, which is located on a JInternalFrame. I've got a code sample that should be working fine on its own; at least it does for me. To replicate the issue, just launch the DragPanelTest class and drag your mouse while over the component with the red border - the panel isn't dragged smoothly, but instead jumps back and forth all the time.
code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.BorderFactory;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class DragPanelTest extends JPanel implements MouseMotionListener, MouseListener {
Point oldDragPt;
boolean dragEnabled;
int newX;
int newY;
public static void main(String[] args) {
System.out.println("Launching Test.java ...");
JFrame frame = new JFrame("Title");
JInternalFrame intFrame = new JInternalFrame("title", true, true, true, true);
JDesktopPane deskPane = new JDesktopPane();
DragPanelTest dragPanel = new DragPanelTest();
frame.setSize(300, 300);;
frame.setLayout(null);
frame.setVisible(true);
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
int x = (d.width - frame.getWidth()) / 2;
int y = (d.height - frame.getHeight()) / 2;
frame.setLocation(x, y);
deskPane.setBounds(50, 50, 200, 200);
deskPane.setBorder(BorderFactory.createLineBorder(Color.BLACK));
frame.add(deskPane);
deskPane.add(intFrame);
intFrame.setSize(150, 150);
intFrame.add(dragPanel);
intFrame.setVisible(true);
}
public DragPanelTest() {
this.setSize(100,100);
this.setBorder(BorderFactory.createLineBorder(Color.RED));
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
public void mousePressed(MouseEvent e) {
if (e.isMetaDown()) {
dragEnabled = true;
oldDragPt = new Point((int) this.getMousePosition().getX(), (int) this.getMousePosition().getY());
}
repaint();
}
public void mouseReleased(MouseEvent e) {
dragEnabled = false;
oldDragPt = null;
}
public void mouseDragged(MouseEvent e) {
if (e.isMetaDown() && this.getMousePosition() != null) {
if (oldDragPt != null) {
if (dragEnabled) {
int mouseX = (int) this.getMousePosition().getX();
int mouseY = (int) this.getMousePosition().getY();
int x = this.getX();
int y = this.getY();
int diffX = (mouseX - (int) oldDragPt.getX());
int diffY = (mouseY - (int) oldDragPt.getY());
newX = getX() + diffX;
newY = getY() + diffY;
this.setLocation(newX, newY);
this.repaint();
oldDragPt = new Point(mouseX, mouseY);
}
} else {
oldDragPt = new Point((int) this.getMousePosition().getX(), (int) this.getMousePosition().getY());
}
}
}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseMoved(MouseEvent e) {}
}
Note that I left out some System.out.println() or #Override commands to shorten the code a bit.
Anyhow, if anyone knows what I'm doing wrong / what to improve, I'd be very thankful!

the panel isn't dragged smoothly, but instead jumps back and forth all the time.
When you have jumping its because the relative movements between the mouse and the component and the parent panel is off. Your code is too complicated to point out the exact line causing the problem.
The basic code for dragging a component around a panel is:
public class DragListener extends MouseInputAdapter
{
Point location;
MouseEvent pressed;
public void mousePressed(MouseEvent me)
{
pressed = me;
}
public void mouseDragged(MouseEvent me)
{
Component component = me.getComponent();
location = component.getLocation(location);
int x = location.x - pressed.getX() + me.getX();
int y = location.y - pressed.getY() + me.getY();
component.setLocation(x, y);
}
}
For more advanced code with more feature you can also check out the Component Mover class found in Moving Windows.

I had a similar problem with moving the viewport for a scrollpane. I think that the when a component is re-positioned, the value of the saved point is relative to the original location and is incorrect relative to the new location. My solution, which seems illogical to me, made the drag operation flow smoothly. What I did is to add the point delta back into the saved location.
This is the basic code to drag the viewport:
Point priorPanPoint;
public void mouseDragged(MouseEvent e) {
Point newPt = subtractPoints(priorPanPoint, e.getPoint());
priorPanPoint = addPoints(e.getPoint(), newPt);
newPt = addPoints(getVisibleRect().getLocation(), newPt);
viewport.setViewPosition(newPt);
}
public void mousePressed(MouseEvent e) {
priorPanPoint = e.getPoint();
}
private Point addPoints(Point p1, Point p2) {
return new Point(p1.x + p2.x, p1.y + p2.y); }
private Point subtractPoints(Point p1, Point p2) {
return new Point(p1.x - p2.x, p1.y - p2.y);
}

Related

JFrame wrong location with Ubuntu (Unity ?)

It seems that there is a bug with Ubuntu (maybe only unity). The decoration of the JFrame is taken into account for getLocation() and getSize(), but not for setLocation() and setSize(). This leads to weird behaviour. For instance, if you use pack() after the frame is displayed and the dimensions changed, the frame will go down 20 pixels...
To illustrate a concrete case when it becomes really annoying, I made a SSCCE. It's a JFrame with a basic JPanel. If you drag the panel, the JFrame is supposed to move along.
Under Windows, it works as expected. Under Ubuntu, if I do setUndecorated(true) it will also work fine, but if I let the decoration, the JFrame turn crazy !
public class Test {
private static JFrame mainFrame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
mainFrame = new JFrame("test");
mainFrame.setSize(300,20);
mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
mainFrame.setVisible(true);
Container pane = mainFrame.getContentPane();
pane.addMouseMotionListener(new MouseMotionListener() {
private int posX = 0, posY = 0;
#Override
public void mouseDragged(MouseEvent e) {
int x = e.getX() - posX + mainFrame.getX();
int y = e.getY() - posY + mainFrame.getY();
mainFrame.setLocation(x, y);
}
#Override
public void mouseMoved(MouseEvent e) {
posX = e.getX();
posY = e.getY();
}
});
}
});
}
}
I don't know how I can fix that. How can I get the size of the windows decoration ? And I have no idea about which versions of Ubuntu are concerned. And if it is only a Unity problem, I don't even know how to find out if my user is using Unity...
Any idea for a workaround ?
Edit :
Ok, MadProgrammer did provide a better code, but the bug still occurs sometimes. I edited my MouseListener accordingly to track the bug :
pane.addMouseMotionListener(new MouseMotionListener() {
private int posX = 0, posY = 0;
#Override
public void mouseDragged(MouseEvent e) {
int x = e.getXOnScreen() - posX;
int y = e.getYOnScreen() - posY;
mainFrame.setLocation(x, y);
System.out.println("drag : border ignored / border considered : "+(mainFrame.getY()+e.getY())+" / "+e.getYOnScreen());
}
#Override
public void mouseMoved(MouseEvent e) {
posX = e.getXOnScreen() - mainFrame.getX();
posY = e.getYOnScreen() - mainFrame.getY();
System.out.println("move : border ignored / border considered : "+e.getY()+" / "+posY);
}
});
Each time that the 2 values are identical, it means that the bug will occur on the next click. Otherwise, the values are different. On other OS, the values are always the same. Actually, they should be or the same always, or always different. I don't understand how they can be sometimes equal and sometimes different...
I don't have Ubuntu to test with, but I've used something similar to this on both MacOS and Windows
import java.awt.Container;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Test {
private static JFrame mainFrame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
mainFrame = new JFrame("test");
mainFrame.setSize(300, 100);
mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
mainFrame.setVisible(true);
Container pane = mainFrame.getContentPane();
MouseAdapter ma = new MouseAdapter() {
private Point offset;
#Override
public void mouseDragged(MouseEvent e) {
if (offset != null) {
Point pos = e.getLocationOnScreen();
int x = pos.x - offset.x;
int y = pos.y - offset.y;
System.out.println(x + "x" + y);
SwingUtilities.getWindowAncestor(e.getComponent()).setLocation(x, y);
}
}
#Override
public void mousePressed(MouseEvent e) {
Point pos = SwingUtilities.getWindowAncestor(e.getComponent()).getLocation();
// Point pos = e.getComponent().getLocationOnScreen();
offset = new Point(e.getLocationOnScreen());
System.out.println(pos + "/" + offset);
offset.x -= pos.x;
offset.y -= pos.y;
System.out.println(offset);
}
};
pane.addMouseListener(ma);
pane.addMouseMotionListener(ma);
}
});
}
}
This should work for both decorated and undecorated windows, as it takes the difference between the positions of the component (on the screen) and the windows current position. When dragged, it calculates the distance of movement from the click point and updates the window's location accordingly (allowing for the original offset of the click)

I need help on moving the small box created to move smoothly as I click and drag near the outside layer. Here is my code: [duplicate]

I made a mini code that draw oval and link each other , now i try to move the oval(Circle) but I have a problem (in coding)
// Panneau.java
public class Panneau extends JPanel {
private int R = 20;
private boolean isDrag = false;
String text = "stack";
int x = 250, y = 200;
int height = 50, width = 50;
Random Rnd = new Random();
int rand=Rnd.nextInt();
int r=Math.abs(rand%250);
int r2=Math.abs(rand%250);
public Panneau() {
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if ((x<=e.getX() && x+R>=e.getX()) && ( y<=e.getY() && y+R>=e.getY())) {
moveVertex(e.getX(),e.getY());
isDrag = true;
}
}
#Override
public void mouseReleased(MouseEvent e) {
isDrag = false;
}
});
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
if (isDrag) moveVertex(e.getX(),e.getY());
}
});
}
private void moveVertex(int x1, int y1) {
if ((x!=x1) || (y!=y1)) {
x=x1-10;
y=y1-10;
repaint();
}
}
#Override
protected void paintComponent(Graphics g){
// declaration
super.paintComponent(g);
g.setColor(Color.black);
g.drawLine(x,y,x+r,y+r2);
g.setColor(Color.yellow);
g.fillOval(x-height/2, y-width/2,width, height);
g.fillOval((x-height/2)+r, (y-width/2)+r2,width, height);
FontMetrics fm = g.getFontMetrics();
double textWidth = fm.getStringBounds(text, g).getWidth();
g.setColor(Color.blue);
g.drawString(text, (int) (x - textWidth/2),(int) (y + fm.getMaxAscent() / 2));
g.drawString(text, (int) (x - textWidth/2)+r,(int) (y + fm.getMaxAscent() / 2)+r2);
}
}
I must move the two circles and the line must not move(Graph node)
please help me and thanks :)
After the update ( thanks to MadProgrammer) now I can move all the figure ( but if I clicked in the red circle only) , I want to move just circles thanks :)
Basically, because instead of using reapint(int, int) you could use repaint()
private void moveVertex(int x1, int y1) {
int OFFSET = 1;
if ((x != x1) || (y != y1)) {
x = x1 - 10;
y = y1 - 10;
repaint();
}
}
This will ensure that the entire component is repainted.
While I wouldn't discount the use of repaint(int, int), because your painting process is relatively simple, it's not going to provide you with a great deal of benefit at this stage
Updated with additional example
IF I understand, you want to be able to move a single node and have the line remain joined.
While it might be possible to implement within the code you have available, a simpler soltution would be to take advantage of the 2D Graphics Shape API, this provides a number of really useful functions, including determining of points fall within a given shape.
It also means you don't need to keep track of a large number of parameters, but instead, get a self contained object that just knows how it should be painted...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestGraphNode {
public static void main(String[] args) {
new TestGraphNode();
}
public TestGraphNode() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Panneau());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Panneau extends JPanel {
private int radius = 50;
private String text = "stack";
private List<Ellipse2D> nodes;
private Ellipse2D dragged;
private Point offset;
public Panneau() {
nodes = new ArrayList<>(25);
nodes.add(new Ellipse2D.Float(50 - (radius / 2), 100 - (radius / 2), radius, radius));
nodes.add(new Ellipse2D.Float(350 - (radius / 2), 100 - (radius / 2), radius, radius));
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
for (Ellipse2D node : nodes) {
if (node.contains(e.getPoint())) {
System.out.println("Clicked...");
dragged = node;
// Adjust for the different between the top/left corner of the
// node and the point it was clicked...
offset = new Point(node.getBounds().x - e.getX(), node.getBounds().y - e.getY());
// Highlight the clicked node
repaint();
break;
}
}
}
#Override
public void mouseReleased(MouseEvent e) {
// Erase the "click" highlight
if (dragged != null) {
repaint();
}
dragged = null;
offset = null;
}
});
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
if (dragged != null && offset != null) {
// Adjust the position of the drag point to allow for the
// click point offset
Point to = e.getPoint();
to.x += offset.x;
to.y += offset.y;
// Modify the position of the node...
Rectangle bounds = dragged.getBounds();
bounds.setLocation(to);
dragged.setFrame(bounds);
// repaint...
repaint();
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
// declaration
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// Draw the connecting lines first
// This ensures that the lines are under the nodes...
Point p = null;
for (Ellipse2D node : nodes) {
g2d.setColor(Color.BLACK);
Point to = node.getBounds().getLocation();
to.x += radius / 2;
to.y += radius / 2;
if (p != null) {
g2d.draw(new Line2D.Float(p, to));
}
p = to;
}
// Draw the nodes...
for (Ellipse2D node : nodes) {
g2d.setColor(Color.yellow);
g2d.fill(node);
if (node == dragged) {
g2d.setColor(Color.BLUE);
g2d.draw(node);
}
g2d.setColor(Color.BLUE);
FontMetrics fm = g.getFontMetrics();
int textWidth = fm.stringWidth(text);
int x = node.getBounds().x;
int y = node.getBounds().y;
int width = node.getBounds().width;
int height = node.getBounds().height;
g.drawString(text,
x + ((width - textWidth)) / 2,
y + ((height - fm.getHeight()) / 2) + fm.getAscent());
}
g2d.dispose();
}
}
}

Drag/Moving a shape around jPanel

Yesterday I ask a question about how to draw a bounding box to hold a shape inside and how to drag and drop that selected shape.
The first question is solved. But I'm having some trouble to move the shape. Is there any especific transformations to move a shape around the jPanel?
I have this code:
public boolean drag(MouseEvent e) {
if(points.isEmpty()) //if point's vector is empty
return false;
if(!selected)
return false;
int x = e.getX(), y = e.getX();
if (!dragging)
lastMovePoint.setLocation(x, y);
dragging = true;
int deslocX = 0;
int deslocY = 0;
int oldX = -1;
int oldY = -1;
int size = points.size();
for(int i = 0; i < size; i++) {
oldX = lastMovePoint.x;
oldY = lastMovePoint.y;
deslocX = x - oldX;
deslocY = y - oldY;
points.set(i, new Point(points.get(i).x + deslocX, points.get(i).y + deslocY));
//set the vector of points so that when there is a repaint() it repaints the shape with the new
//coordinates
}
lastMovePoint.setLocation(x, y); //set the location of the old point
return true;
}
This method is called by the listener mouseDragged and return true in case of sucess. What I'm trying to do is to add the difference between the previous point of dragg and the actual.
When I run this code I have a problem:
The shape only goes to right/left, up and down is not working...
.
Yes, in order to drag components you also need to know the starting location so you can calculate the distance the mouse has moved.
Here is some code that shows this general behaviour for moving a window.
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
public class MoveWindow extends MouseInputAdapter
{
Point location;
MouseEvent pressed;
public void mousePressed(MouseEvent me)
{
pressed = me;
}
public void mouseDragged(MouseEvent me)
{
Component component = me.getComponent();
location = component.getLocation(location);
int x = location.x - pressed.getX() + me.getX();
int y = location.y - pressed.getY() + me.getY();
component.setLocation(x, y);
}
private static void createAndShowGUI()
{
JWindow window = new JWindow();
window.setSize(300, 300);
window.setLocationRelativeTo( null );
window.setVisible(true);
MouseInputAdapter listener = new MoveWindow();
window.addMouseListener( listener );
window.addMouseMotionListener( listener );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
I'll let you implement it for your purposes.
int x = e.getX(), y = e.getX();
This should probably be changed to
int x = e.getX(), y = e.getY();
That's why it only works in the x direction, you aren't actually taking into account the Y direction

Moving a JLabel with an ImageIcon dynamically, jumps all around the frame

I have a Java Swing question. I am currently coding the game checkers as an exercise. I created the checkers pieces as JLabels with an ImageIcon of the checkers piece. I added a MouseListener to update the x and y locations of the label on the frame, and am using an ActionListener to set the label location every 5 milliseconds based on a timer. It works except that the label jumps around the screen when I am dragging it with the mouse (instead of tracking with the mouse).
Does anybody know what is causing this? Is there an easy solution based on the code I currently have?
I am new to swing stuff so I realize I could be taking the wrong approach entirely. I am unable to attach the image, but it is just a 80 x 80 px square with a solid black circle in the foreground and the background is transparent. Thanks a lot!
package checkers;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.*;
public class Piece2 extends JLabel implements
ActionListener, MouseListener, MouseMotionListener{
ImageIcon checkerIcon;
String color = null;
Timer t = new Timer(5, this);
int x, y, width = 80, height = 80;
public Piece2(int initX, int initY, String color){
this.x = initX;
this.y = initY;
this.color = color;
t.start();
setLocation(x, y);
setSize(width, height);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
addMouseMotionListener(this);
addMouseListener(this);
if(color.equals("Red")){
checkerIcon = new ImageIcon("/RedChecker.png");
}
else if(color.equals("Black")){
checkerIcon = new ImageIcon("/BlackChecker.png");
}
setIcon(checkerIcon);
}
#Override
public void actionPerformed(ActionEvent e) {
setLocation(x, y);
}
/**
* Mouse Event Listeners
*
*/
#Override
public void mouseDragged(MouseEvent e) {
this.x = e.getX();
this.y = e.getY();
}
#Override
public void mouseReleased(MouseEvent e) {
x = x - (x % 50);
y = y - (y % 50);
}
#Override
public void mouseMoved(MouseEvent e) {}
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
//To test Piece
public static void main(String[] args){
Piece2 p1 = new Piece2(0, 0, "Black");
JFrame frame1 = new JFrame("Test");
frame1.setSize(800, 800);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.add(p1);
frame1.setVisible(true);
}
}

a brush effect in java

I want to achieve the effect of java in the brush, the mouse returned to the point, move the mouse faster, thinner lines.
Here is a program which demonstrates what you are trying to achieve.
This program is not perfect, but should get you started with what you are trying to build.
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
public class PaintBrush {
JFrame f;
Canvas c;
int x=-1, y=-1;
public PaintBrush() {
f = new JFrame();
f.setSize(600, 400);
c = new Canvas() {
public void paint(Graphics g) {
// super.paint(g);
}
};
f.getContentPane().add(c);
c.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseDragged(MouseEvent e) {
if(x==-1){
x = e.getX();
y = e.getY();
}
int diffx = Math.abs(x - e.getX());
int diffy = Math.abs(y - e.getY());
System.out.println("diffx:"+diffx+"\t"+"diffy:"+diffy);
int speed = (int) Math.sqrt((diffx + diffy));
if(speed>1){
c.getGraphics().fillOval(x, y, 20-speed*2, 20-speed*2);
}else {
c.getGraphics().fillOval(x, y, 20, 20);
}
System.out.print("Speed:"+speed + "\t");
System.out.println("x:"+e.getX());
x = e.getX();
y = e.getY();
}
});
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
public static void main(String[] args) {
new PaintBrush();
}

Categories