The blue label is meant to move when you click and drag it. This works but the x / y position then jumps in a funny way.
Here's the code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class test extends JFrame implements MouseListener, MouseMotionListener {
private JPanel panel = new JPanel(null);
private JLabel label1 = new JLabel();
private JLabel label2 = new JLabel();
private int mouseX = 200;
private int mouseY = 100;
private boolean drag = false;
public test() {
this.add(panel);
panel.setBackground(Color.WHITE);
panel.add(label1);
label1.setOpaque(true);
label1.setBackground(Color.BLUE);
label1.setBounds(mouseX, mouseY, 100, 50);
label1.addMouseMotionListener(this);
label1.addMouseListener(this);
panel.add(label2);
label2.setOpaque(true);
label2.setBackground(Color.RED);
label2.setBounds(mouseX + 200, mouseY, 100, 50);
label2.addMouseMotionListener(this);
label2.addMouseListener(this);
}
#Override
public void mousePressed(MouseEvent e) {
if (e.getSource() == label1) {
drag = true;
}
}
#Override
public void mouseReleased(MouseEvent e) {
drag = false;
}
#Override
public void mouseDragged(MouseEvent e) {
if (drag == true) {
mouseX = e.getX();
mouseY = e.getY();
label1.setBounds(mouseX, mouseY, 100, 50);
}
}
public void mouseMoved(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public static void main(String[] args) {
test frame = new test();
frame.setVisible(true);
frame.setSize(600, 400);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Just put this on your MouseDragged method:
public void mouseDragged(MouseEvent e)
{
if (drag == true)
{
JComponent jc = (JComponent)e.getSource();
jc.setLocation(jc.getX()+e.getX(), jc.getY()+e.getY());
}
}
The coordinates returned by MouseEvent::getX() and MouseEvent::getY() represent the location of the event relative to the event's subject (i.e. relative to the label itself), which explains why your solution results in the label erratically jumping.
By using MouseEvent::getComponent() to grab the label and then querying its position (possibly relative to the position when dragging commenced), you can devise a working solution.
Your problem is your setting your bounds based on the mouse location in the MouseListener, but the MouseListener has its location relative to the JLabel itself, but the JLabel's location should be set relative to the panel. You'll need to do some simple vector addition to figure this out.
edit: oops, I didn't see that this was already answered, and they say the same thing... sorry.
Maybe try adding something like that
The red one will do it better
private int clicX = 0;
private int clicY = 0;
public void mousePressed(MouseEvent e) {
drag = true;
if (e.getSource() == label1) {
}
if (e.getSource() == label2) {
clicX = e.getX();
clicY = e.getY();
}
}
public void mouseDragged(MouseEvent e) {
if (e.getSource() == label2) {
JComponent jc = (JComponent)e.getSource();
jc.setLocation(jc.getX()+e.getX()-clicX, jc.getY()+e.getY()-clicY);
}
Create two global variables:
int x_pressed = 0;
int y_pressed = 0;
then create two events (mousePressed and mouseDragged over JLabel):
lbl_banner.addMouseListener(new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e) {
//catching the current values for x,y coordinates on screen
x_pressed = e.getX();
y_pressed = e.getY();
}
});
lbl_banner.addMouseMotionListener(new MouseMotionAdapter(){
#Override
public void mouseDragged(MouseEvent e){
//and when the Jlabel is dragged
setLocation(e.getXOnScreen() - x_pressed, e.getYOnScreen() - y_pressed);
}
});
Related
I'm trying to create a MS Paint-like software with Java Choice Menus, but I'm experiencing some difficulties with two functions in particular
I get a massive wall of error text upon selecting a colour from my Colour Menu. What I wish to achieve is selecting the colour converts the string containing the option into lower case, before setting the PenColour into that option
Drawing Lines don't seem to work, despite the same code being functional in a different applet that didn't use Choice. I want it to select two points which then have a line drawn between them.
Here is my Code
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.awt.Choice;
public class PockettProjectTest1 extends Applet implements ActionListener, MouseListener, MouseMotionListener{
//Coordinates and sizes used to draw GUI
private final int GUI_HEIGHT = 100;
private final int GUI_START_X = 10;
private final int GUI_START_Y = 10;
private final int FIELD_SIZE = 11; //Numbers of characters for text field display
////////////////////////GLOBAL VARIABLES////////////////////////
private Dimension appletSize; //used to encapsulate the Applets dimensions
private int x, y, appletHeight, appletWidth, PenThick, PenMode, ShapeMode, ClickXO, ClickYO, ClickXN, ClickYN, ClickCount;
private String SizeString;
//a Panel is a container used here to hold and position the GUI components
private Panel guiPanel;
private Color PenColour;
//GUI components cv
public void init(){
//Disables the applet layout manager and stops automatic GUI component positioning
setLayout(null);
setBackground(Color.white);
addMouseListener(this);
addMouseMotionListener(this);
addMouseListener(this);
ClickCount = 0;
ClickXO = 0;
ClickYO = 0;
ClickXN = 0;
ClickYN = 0;
PenMode = 0;
ShapeMode = 0;
appletSize = this.getSize(); //captures the Applet dimensions
appletHeight = appletSize.height; //captures the Applet height
appletWidth = appletSize.width; //captures the Applet width
//Set up the Panel ready to hold the GUI components
guiPanel = new Panel();
guiPanel.setLocation(0, 0);
guiPanel.setSize(appletWidth,GUI_HEIGHT);
guiPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 40));
guiPanel.setBackground(Color.lightGray);
add(guiPanel);
Choice mode = new Choice();
Choice size = new Choice();
Choice color = new Choice();
Choice shape = new Choice();
mode.add("Pen");
mode.add("Eraser");
mode.add("Fill");
mode.add("Shape");
size.add("01px");
size.add("02px");
size.add("03px");
size.add("05px");
size.add("10px");
size.add("15px");
size.add("20px");
size.add("30px");
size.add("50px");
shape.add("N/A");
shape.add("Line");
shape.add("Oval");
shape.add("Oblong");
shape.add("Square");
shape.add("Circle");
shape.add("FillOval");
shape.add("FillOblong");
shape.add("FillSquare");
shape.add("FillCircle");
//add choice or combobox
guiPanel.add(mode);
guiPanel.add(size);
guiPanel.add(shape);
PenThick = 1;
PenColour = Color.black;
mode.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent ie)
{
PenMode = mode.getSelectedIndex();
}
});
size.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent ie)
{
SizeString = size.getSelectedItem();
PenThick = Integer.parseInt(SizeString.substring(0,2));
Graphics g = getGraphics();
}
});
//// Start of Issue 1:
colour.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent ie)
{
ColourString = colour.getSelectedItem();
ColourString.toLowerCase();
Color PenColour.getColor(ColourString);
}
});
/// End of Issue 1
shape.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent ie)
{
ShapeMode = shape.getSelectedIndex();
}
});
}
public void actionPerformed(ActionEvent e) {
}
////Issue 2:
public void mouseReleased(MouseEvent me){
if (PenMode == 3) {
if (ClickCount == 0) {
ClickXO = me.getX();
ClickYO = me.getY();
ClickCount ++;
System.out.println(ClickCount);
return;
}
if (ClickCount == 1) {
ClickXN = me.getX();
ClickYN = me.getY();
System.out.println(ClickCount);
Graphics g = getGraphics();
g.setColor(Color.black);
g.drawLine(ClickXO, ClickYO, ClickXN, ClickYN);
System.out.println(ClickXO);
System.out.println(ClickYO);
System.out.println(ClickXN);
System.out.println(ClickYN);
ClickCount = 0;
}
}
}
/// End of Issue 2
public void mousePressed(MouseEvent e){
}
public void mouseDragged(MouseEvent e){
if (PenMode == 0){
Graphics g = getGraphics();
g.setColor(Color.black);
g.fillOval(e.getX(), e.getY(), PenThick, PenThick);
}
if (PenMode == 1){
Graphics g = getGraphics();
g.setColor(Color.white);
g.fillRect(e.getX(), e.getY(), PenThick, PenThick);
}
}
public void mouseMoved(MouseEvent e){
}
public void mouseExited(MouseEvent e){
}
public void mouseEntered(MouseEvent e){
}
public void mouseClicked(MouseEvent e){
}
}
I have to have this code due by the end of Friday, so I hope I can get the issue solved by then.
I'm experimenting on a GUI that I programmed and I don't understand how I can fix my problem:
My GUI contains a jPanel that on receiving a mouseclick, paints a point with filloval command.
private void myPnlMousePressed(java.awt.event.MouseEvent evt) {
changed = true;
p.x = evt.getX();
p.y = evt.getY();
drewPoints(p.x, p.y);
}
private void drewPoints (int x, int y) {
if (gf == null) {
gf = (Graphics)myPnl.getGraphics();
}
myPointsList.add(new Point(x, y));
gf.fillOval(x, y, 5, 5);
xVal.setText("X = " + x);
yVal.setText("Y = " + y);
}
everything works fine but when I want to open an XML file that I created to save all the points it doesn't work.
The problem is that when I use the repaint method on the jPanel after choosing a file, all the points loads fine but the panel can't draw the points.
If I put the repaint method in the open button listener (before the choosing file) it works, but then if the user cancels the open option so the panel stays blank and I don't want to draw the points again.
I think it happens because the repaint process is not finished.
All the points added to a private List.
private void OpenFile() {
try {
File thisFile;
JFileChooser of = new JFileChooser();
int option = of.showOpenDialog(of);
if (option == JFileChooser.APPROVE_OPTION){
thisFileName = of.getSelectedFile().getPath();
thisFile = new File(thisFileName);
if (!of.getSelectedFile().getName().endsWith(".xml")) {
String error = "Error, You didn't select XML file";
JOptionPane.showMessageDialog(this, error, "Wrong type of file", JOptionPane.INFORMATION_MESSAGE);
return;
}
myPnl.repaint();
myPointsList.clear();
....
....
....
for (int i = 0; i < pointsList.getLength(); i++) {
Element point = (Element) pointsList.item(i);
p.x = Integer.parseInt(point.getElementsByTagName("X").item(0).getTextContent());
p.y = Integer.parseInt(point.getElementsByTagName("Y").item(0).getTextContent());
drewPoints(p.x, p.y);
}
....
how can I make it work??
Don't use gf = (Graphics)myPnl.getGraphics();, this is not how painting in Swing works. The getGraphics method can return null and is nothing more then a snap shot of the last paint cycle, any thing you paint to it will be erased on the next paint cycle (repaint).
Instead, override the JPanels paintComponent and put all you painting logic there. There is an expectation that when called, you are expected to fully re-paint the current state of the component.
See Painting in AWT and Swing and Performing Custom Painting for more details about how painting works in Swing
You have to use the repaint() and override the paint() method:
class MyPanel extends JPanel implements MouseListener
{
private int x;
private int y;
public MyPanel() {
super();
addMouseListener(this);
}
#Override public void mouseEntered(MouseEvent e) { }
#Override public void mouseExited(MouseEvent e) { }
#Override public void mouseClicked(MouseEvent e) { }
#Override public void mousePressed(MouseEvent e) { }
#Override public void mouseReleased(MouseEvent e) {
x = e.getX();
y = e.getY();
repaint();
}
#Override public void paint(Graphics g) {
super.paint(g);
g.fillOval(x, y, 10, 10);
}
}
If you want to draw all points, don't use x and y but a list of points:
class MyPanel extends JPanel implements MouseListener
{
private ArrayList<Point> points = new ArrayList<>();
public MyPanel() {
super();
addMouseListener(this);
}
#Override public void mouseEntered(MouseEvent e) { }
#Override public void mouseExited(MouseEvent e) { }
#Override public void mouseClicked(MouseEvent e) { }
#Override public void mousePressed(MouseEvent e) { }
#Override public void mouseReleased(MouseEvent e) {
points.add(new Point(e.getX(), e.getY()));
repaint();
}
#Override public void paint(Graphics g) {
super.paint(g);
for (Point p : points)
g.fillOval(p.getX(), p.getY(), 10, 10);
}
}
where:
class Point
{
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
Then use it:
public static void main(String[] args) {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
MyPanel myPanel = new MyPanel();
frame.add(myPanel);
frame.setVisible(true);
}
In Chapter 15 of Liang's Intro to Java Programming (7th ed.), he introduces a program to make a (2-D) ball on a JPanel and enlarge it upon clicking enlarge/shrink buttons. I've modified the program so that it also 1) enlarges/shrinks the ball if the user clicks/option+clicks, 2) allows you to pick the color of the ball by pressing a button, and 3) allows you to move the circle by dragging it with your mouse.
The last modification is what was giving me trouble for a while, because I wanted to center the ball at the beginning, but then allow the user to move the ball around with the mouse. The solution I came up with was to have the paintComponent method only set the x- and y-coordinates of the ball relative to getWidth() and getHeight() the first time it paints. To do that, I added a paintCount variable to the BallCanvas class and made an if statement so that it would only execute the first time around. When I was trying to figure out how to do this initially, I saw other solutions, like the ones given here: Why can't I access my panel's getWidth() and getHeight() functions? , but I find my solution much simpler.
So the question is: is what I did considered bad coding style? Would a professional programmer scoff at this solution? Or is it OK?
More importantly, is there a better (but also, relatively simple) way to do this that doesn't involve setting up a counter?
Here are the relevant bits of code:
The beginning of BallCanvas:
public static class BallCanvas extends JPanel {
private int radius = 20;
private Color color = Color.BLACK;
private int ballX;
private int ballY;
private int paintCount = 0;
...
The move method (which responds to a MouseDragged event):
public void move(MouseEvent e){
ballX = e.getX() - radius;
ballY = e.getY() - radius;
repaint();
}
The paintComponent method:
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(color);
if(paintCount < 1){
ballX = getWidth()/2 - radius;
ballY = getHeight()/2 - radius;
}
g.fillOval(ballX, ballY, 2*radius, 2*radius);
paintCount++;
}
Full program:
// Reference: Liang's Intro to Java Programming
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ControlBall extends JFrame{
private JButton jbtRed = new JButton("Red");
private JButton jbtGreen = new JButton("Green");
private JButton jbtBlue = new JButton("Blue");
private JButton jbtBlack = new JButton("Black");
private BallCanvas canvas = new BallCanvas();
private JMenuBar menuBar = new JMenuBar();
private JMenu menu = new JMenu("Edit");
private JMenuItem miEnlarge = new JMenuItem("Enlarge");
private JMenuItem miShrink = new JMenuItem("Shrink");
public ControlBall(){
menuBar.add(menu);
menu.add(miEnlarge);
menu.add(miShrink);
JPanel panel = new JPanel();
panel.add(jbtRed);
panel.add(jbtGreen);
panel.add(jbtBlue);
panel.add(jbtBlack);
this.add(canvas, BorderLayout.CENTER);
this.add(panel, BorderLayout.SOUTH);
this.add(menuBar, BorderLayout.NORTH);
jbtRed.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
canvas.setColor(Color.RED);
}
});
jbtGreen.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
canvas.setColor(Color.GREEN);
}
});
jbtBlue.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
canvas.setColor(Color.BLUE);
}
});
jbtBlack.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
canvas.setColor(Color.BLACK);
}
});
miEnlarge.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
canvas.enlarge();
}
});
miShrink.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
canvas.shrink();
}
});
canvas.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e){
canvas.changeSize(e);
}
public void mousePressed(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
});
canvas.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
canvas.move(e);
}
});
}
public static void main(String[] args){
JFrame frame = new ControlBall();
frame.setTitle("ControlBall");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
frame.setVisible(true);
}
public static class BallCanvas extends JPanel {
private int radius = 20;
private Color color = Color.BLACK;
private int ballX;
private int ballY;
private int paintCount = 0;
public BallCanvas(){
System.out.println(getWidth() + " " + getHeight());
}
public BallCanvas(int initialRadius){
radius = initialRadius;
}
public void setColor(Color color){
this.color = color;
repaint();
}
public void changeSize(MouseEvent e){
int numClicks = e.getClickCount();
if(e.isAltDown()){
if(radius >= 6){
this.radius -= 5*numClicks;
} else{
// do nothing
}
} else{
this.radius += 5*numClicks;
}
repaint();
}
public void enlarge(){
this.radius += 5;
repaint();
}
public void shrink(){
if(radius >= 10){
this.radius -= 5;
}
repaint();
}
public void move(MouseEvent e){
ballX = e.getX() - radius;
ballY = e.getY() - radius;
repaint();
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(color);
if(paintCount < 1){
ballX = getWidth()/2 - radius;
ballY = getHeight()/2 - radius;
}
g.fillOval(ballX, ballY, 2*radius, 2*radius);
paintCount++;
}
}
}
Several things merit attention:
Override getPreferredSize() to establish the panel's initial geometry.
Use that geometry to establish the ball's initial position.
Invoke pack() and then set the location & visibility.
Use Action to encapsulate code shared by menus and controls.
Use adapters consistently.
Use initial threads correctly.
See this Q&A, which examines a related example from several perspectives.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ControlBall extends JFrame {
private JButton jbtRed = new JButton("Red");
private JButton jbtGreen = new JButton("Green");
private JButton jbtBlue = new JButton("Blue");
private JButton jbtBlack = new JButton("Black");
private BallCanvas canvas = new BallCanvas();
private JMenuBar menuBar = new JMenuBar();
private JMenu menu = new JMenu("Edit");
private JMenuItem miEnlarge = new JMenuItem("Enlarge");
private JMenuItem miShrink = new JMenuItem("Shrink");
public ControlBall() {
menuBar.add(menu);
menu.add(miEnlarge);
menu.add(miShrink);
JPanel panel = new JPanel();
panel.add(jbtRed);
panel.add(jbtGreen);
panel.add(jbtBlue);
panel.add(jbtBlack);
this.add(canvas, BorderLayout.CENTER);
this.add(panel, BorderLayout.SOUTH);
this.add(menuBar, BorderLayout.NORTH);
jbtRed.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
canvas.setColor(Color.RED);
}
});
jbtGreen.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
canvas.setColor(Color.GREEN);
}
});
jbtBlue.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
canvas.setColor(Color.BLUE);
}
});
jbtBlack.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
canvas.setColor(Color.BLACK);
}
});
miEnlarge.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
canvas.enlarge();
}
});
miShrink.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
canvas.shrink();
}
});
canvas.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
canvas.changeSize(e);
}
#Override
public void mouseDragged(MouseEvent e) {
canvas.move(e);
}
});
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new ControlBall();
frame.setTitle("ControlBall");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class BallCanvas extends JPanel {
private static final int SIZE = 400;
private int radius = 20;
private Color color = Color.BLACK;
private int ballX = SIZE / 2 - radius;
private int ballY = SIZE / 2 - radius;
public BallCanvas() {
System.out.println(getWidth() + " " + getHeight());
}
public BallCanvas(int initialRadius) {
radius = initialRadius;
}
public void setColor(Color color) {
this.color = color;
repaint();
}
public void changeSize(MouseEvent e) {
int numClicks = e.getClickCount();
if (e.isAltDown()) {
if (radius >= 6) {
this.radius -= 5 * numClicks;
} else {
// do nothing
}
} else {
this.radius += 5 * numClicks;
}
repaint();
}
public void enlarge() {
this.radius += 5;
repaint();
}
public void shrink() {
if (radius >= 10) {
this.radius -= 5;
}
repaint();
}
public void move(MouseEvent e) {
ballX = e.getX() - radius;
ballY = e.getY() - radius;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillOval(ballX, ballY, 2 * radius, 2 * radius);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
}
}
I have a program where I am creating a JLabel on a click and then dragging that to another portion of the interface.
What I want is to click somewhere in the JPanel, have it drop a JLabel there, and then drag another JLabel all in the same click.
I am able to do this, but it takes multiple clicks. Can I do it in one click?
To illustrate what I mean, I created this sample program:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DragTest extends JFrame implements MouseMotionListener,
MouseListener {
private JPanel panel = new JPanel(null);
private JLabel dragLabel = new JLabel("drag");;
private final JWindow window = new JWindow();
public DragTest() {
this.add(panel);
panel.addMouseListener(this);
dragLabel.setFont(new Font("Serif", Font.BOLD, 48));
}
#Override
public void mouseDragged(MouseEvent me) {
dragLabel = new JLabel("drag");
dragLabel.setFont(new Font("Serif", Font.BOLD, 48));
int x = me.getPoint().x;
int y = me.getPoint().y;
window.add(dragLabel);
window.pack();
Point pt = new Point(x, y);
Component c = (Component) me.getSource();
SwingUtilities.convertPointToScreen(pt, c);
window.setLocation(pt);
window.setVisible(true);
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
JLabel dropLabel = new JLabel("drop");
panel.add(dropLabel);
dropLabel.setForeground(Color.RED);
dropLabel.setFont(new Font("Serif", Font.BOLD, 48));
dropLabel.setBounds(e.getX(), e.getY(), 100, 60);
dropLabel.addMouseMotionListener(this);
dropLabel.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
dragLabel.setVisible(false);
window.setVisible(false);
}
});
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
public static void main(String[] args) {
DragTest frame = new DragTest();
frame.setVisible(true);
frame.setSize(600, 400);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
So the initial click creates the "drop" JLabel, and then clicking on and dragging on the "drop" JLabel will create a "drag" JLabel that follows the mouse around.
How can I do this in one click and drag?
Don't create a new JLabel in the mouseDragged method but rather use the same JLabel. For example:
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class DragTest2 extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = 400;
private static final float LABEL_PTS = 24f;
private static final String LABEL_TEXT = "My Label";
private JLabel label = null;
public DragTest2() {
setLayout(null);
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
label = new JLabel(LABEL_TEXT);
label.setFont(label.getFont().deriveFont(LABEL_PTS));
Dimension labelSize = label.getPreferredSize();
label.setSize(labelSize);
int x = e.getX() - labelSize.width / 2;
int y = e.getY() - labelSize.height / 2;
label.setLocation(x , y);
add(label);
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
if (label != null) {
Dimension labelSize = label.getPreferredSize();
int x = e.getX() - labelSize.width / 2;
int y = e.getY() - labelSize.height / 2;
label.setLocation(x , y);
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (label != null) {
Dimension labelSize = label.getPreferredSize();
int x = e.getX() - labelSize.width / 2;
int y = e.getY() - labelSize.height / 2;
label.setLocation(x , y);
repaint();
label = null;
}
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DragTest2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DragTest2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I searched the web for examples of draggable Swing components,
but I found either incomplete or non-working examples.
What I need is a Swing component that can be dragged by the mouse
inside an other component. While being dragged, it should already
change its position, not just 'jump' to its destination.
I would appreciate examples which work without non-standard APIs.
Thank you.
I propose a simple, but well-working solution, found out by myself ;)
What do I do?
When mouse is pressed, I record the cursor's position on screen, and
the component's position.
When mouse is dragged, I calculate the difference between new and
old cursor's position on screen, and move the
component by this difference.
Tested with latest JDK 6 unter Linux (OpenSuse, KDE3),
but hey, it's Java Swing, should work equally everywhere.
Here goes the code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class MyDraggableComponent
extends JComponent {
private volatile int screenX = 0;
private volatile int screenY = 0;
private volatile int myX = 0;
private volatile int myY = 0;
public MyDraggableComponent() {
setBorder(new LineBorder(Color.BLUE, 3));
setBackground(Color.WHITE);
setBounds(0, 0, 100, 100);
setOpaque(false);
addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) { }
#Override
public void mousePressed(MouseEvent e) {
screenX = e.getXOnScreen();
screenY = e.getYOnScreen();
myX = getX();
myY = getY();
}
#Override
public void mouseReleased(MouseEvent e) { }
#Override
public void mouseEntered(MouseEvent e) { }
#Override
public void mouseExited(MouseEvent e) { }
});
addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
int deltaX = e.getXOnScreen() - screenX;
int deltaY = e.getYOnScreen() - screenY;
setLocation(myX + deltaX, myY + deltaY);
}
#Override
public void mouseMoved(MouseEvent e) { }
});
}
}
public class Main {
public static void main(String[] args) {
JFrame f = new JFrame("Swing Hello World");
// by doing this, we prevent Swing from resizing
// our nice component
f.setLayout(null);
MyDraggableComponent mc = new MyDraggableComponent();
f.add(mc);
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setVisible(true);
}
}
Also, I found out that one could create an JInternalFrame inside an JFrame,
but the problem is: you get always an annoying window title bar.
To disable the title bar, sadly, a dirty workaround is necessary:
public class MyDraggableComponent extends JInternalFrame {
public MyDraggableComponent() {
InternalFrameUI thisUI = getUI();
if (thisUI instanceof BasicInternalFrameUI) {
((BasicInternalFrameUI) thisUI).setNorthPane(null);
}
}
I really miss a method like "someInternalFrame.setWindowTitleBar(false)"...
:'(