Gigi Bayte 2
Gigi Bayte 2

Reputation: 984

JFrame dragging from custom button not smooth

I am using practically the same method for moving the JFrame as Java - Custom shaped draggable JFrame

I have a class that extends JPanel. In the said class, I have the previous x and y set to variables like so:

private int pressX, pressY

Then, in the mousePressed method, I have:

pressX = e.getX();
pressY = e.getY();

Finally, in mouseDragged, I have:

mainFrame.setLocation((int) Math.round(mainFrame.getLocation().getX() + e.getX() - pressX), (int) Math.round(mainFrame.getLocation().getY() + e.getY() - pressY));

However, when dragging the window around, there is a fair amount of lag or some kind of problem. Here is a video to show what happens visually.

https://i.sstatic.net/dqrmf.jpg

I am using the swing library and am repainting using a Timer that ticks roughly every two milliseconds.

Edit:

I modified the code such that the points were relative to the JPanel, but the problem still occurs.

dragMe = new Draggable() {
    private static final long serialVersionUID = 1L;

    private Point press;

    @Override
    public void mouseClicked(MouseEvent e) {}

    @Override
    public void mousePressed(MouseEvent e) {
        press = SwingUtilities.convertPoint(this, e.getPoint(), mainFrame);
    }

    @Override
    public void mouseReleased(MouseEvent e) {}

    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) {}

    @Override
    public void mouseDragged(MouseEvent e) {
        Point drag = SwingUtilities.convertPoint(this, e.getPoint(), mainFrame);
        mainFrame.setLocation((int) Math.round(mainFrame.getLocation().getX() + drag.getX() - press.getX()), (int) Math.round(mainFrame.getLocation().getY() + drag.getY() - press.getY()));
    }

    @Override
    public void mouseMoved(MouseEvent e) {}
};

Edit 2:

Unfortunately, the example I have created here works perfectly, and I don't know why it is not working in the true code. I even tried copying the exact class I made in this example into the real application, and the problem still occurs in the true code.

It looks like I will have to look more into this.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
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.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.WindowConstants;

public class Test extends JPanel implements ActionListener {
    private static final long serialVersionUID = 1L;

    private static JFrame frame;

    public static void main(String[] args) {

        class Draggable extends JPanel implements MouseListener, MouseMotionListener {
            private static final long serialVersionUID = 1L;

            private Point press;

            public Draggable() {
                addMouseListener(this);
                addMouseMotionListener(this);
            }

            @Override
            public void mouseClicked(MouseEvent e) {}

            @Override
            public void mousePressed(MouseEvent e) {
                press = SwingUtilities.convertPoint(this, e.getPoint(), frame);
            }

            @Override
            public void mouseReleased(MouseEvent e) {}

            @Override
            public void mouseEntered(MouseEvent e) {}

            @Override
            public void mouseExited(MouseEvent e) {}

            @Override
            public void mouseDragged(MouseEvent e) {
                Point drag = SwingUtilities.convertPoint(this, e.getPoint(), frame);
                frame.setLocation((int) Math.round(frame.getLocation().getX() + drag.getX() - press.getX()), (int) Math.round(frame.getLocation().getY() + drag.getY() - press.getY()));
            }

            @Override
            public void mouseMoved(MouseEvent e) {}

        }

        Test t = new Test();
        t.setBounds(0, 0, 1200, 600);
        t.setVisible(true);

        Draggable drag = new Draggable();
        drag.setBounds(24, 24, 24, 24);
        drag.setVisible(true);

        Timer repaintTimer = new Timer(2, t);

        frame = new JFrame();
        frame.setSize(1200, 600);

        frame.add(t);
        frame.add(drag);

        Dimension dim = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setLocation((dim.width - frame.getWidth()) / 2, (dim.height - frame.getHeight()) / 2);
        frame.getContentPane().setLayout(null);
        frame.setAlwaysOnTop(true);
        frame.setResizable(false);

        repaintTimer.start();

        frame.setVisible(true);
        frame.requestFocus();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.BLACK);
        g.fillRect(24, 24, 24, 24);
    }

}

Upvotes: 0

Views: 481

Answers (1)

camickr
camickr

Reputation: 324128

Check out Moving Windows for a class that does this for you.

It is more complicated than the basic dragging logic because it supports other features which you may not need.

The basic logic (from the above class) for dragging a frame when adding the listeners to one (or more) components of the frame would be:

MouseInputAdapter mia = new MouseInputAdapter()
{
    Point location;
    Point pressed;

    public void mousePressed(MouseEvent me)
    {
        pressed = me.getLocationOnScreen();
        Window window = SwingUtilities.windowForComponent(me.getComponent());
        location = window.getLocation();
    }

    public void mouseDragged(MouseEvent me)
    {
        Point dragged = me.getLocationOnScreen();
        int x = (int)(location.x + dragged.getX() - pressed.getX());
        int y = (int)(location.y + dragged.getY() - pressed.getY());
        Window window = SwingUtilities.windowForComponent(me.getComponent());
        window.setLocation(x, y);
     }
};

panel.addMouseListener(mia);
panel.addMouseMotionListener(mia);

Edit:

Here is some more generic code that allows you to move a component directly or indirectly by specify a parent component to receive the events:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class BasicComponentMover extends MouseInputAdapter
{
    /*
    **  A class that allows you to drag a component around a panel.
    **  Optionally you can specify a "parent" component, in which case
    **  the MouseEvents will be converted to be relative to this component
    **  which will then be dragged. This will allow you to drag a JFrame by
    **  dragging a component that has been added to the frame.
    */
    private Component parent;
    private Component destination;
    private Point pressed;

    BasicComponentMover() {}
    {
    }

    BasicComponentMover(Component parent)
    {
        this.parent = parent;
    }

    @Override
    public void mousePressed(MouseEvent me)
    {
        destination = (parent == null) ? me.getComponent() : parent;
        pressed = SwingUtilities.convertPoint(me.getComponent(), me.getPoint(), destination);
    }

    @Override
    public void mouseDragged(MouseEvent me)
    {
        Point location = destination.getLocation();
        Point drag = SwingUtilities.convertPoint(me.getComponent(), me.getPoint(), destination);
        int x = (int)(location.x - pressed.getX() + drag.getX());
        int y = (int)(location.y - pressed.getY() + drag.getY());
        destination.setLocation(x, y);
    }

    private static void createAndShowGUI()
    {
        JPanel background = new JPanel(null);
        background.setPreferredSize(new Dimension(300, 300));
        background.setSize(background.getPreferredSize());
        background.setBackground(Color.RED);

        JPanel foreground = new JPanel(null);
        foreground.setPreferredSize(new Dimension(50, 50));
        foreground.setSize(foreground.getPreferredSize());
        foreground.setBackground(Color.BLUE);
        background.add(foreground);

        JFrame frame = new JFrame("BasicComponentMover");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(null);
        frame.add(background);
        frame.setSize(500, 500);
        frame.setLocationByPlatform( true );
        frame.setVisible( true );

        //  Drag frame by dragging the red background panel

        BasicComponentMover bcm1 = new BasicComponentMover(frame);
        background.addMouseListener(bcm1);
        background.addMouseMotionListener(bcm1);

        // Drag the blue forground component around the red background

        BasicComponentMover bcm2 = new BasicComponentMover();
        foreground.addMouseListener(bcm2);
        foreground.addMouseMotionListener(bcm2);

        //  Or, drage the background around the frame

        BasicComponentMover bcm3 = new BasicComponentMover(background);
//      foreground.addMouseListener(bcm3);
//      foreground.addMouseMotionListener(bcm3);
    }

    public static void main(String[] args) throws Exception
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }
}

Try dragging the blue/red components to see the difference.

Upvotes: 1

Related Questions