从自定义按钮拖动 JFrame 不流畅

JFrame dragging from custom button not smooth

我使用与 Java - Custom shaped draggable JFrame

几乎相同的方法移动 JFrame

我有一个扩展 JPanel 的 class。在上述 class 中,我将之前的 x 和 y 设置为如下变量:

private int pressX, pressY

然后,在 mousePressed 方法中,我有:

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

最后,在 mouseDragged 中,我有:

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

但是,拖动 window 时,会出现相当大的延迟或某种问题。这是一个视频,展示了视觉上发生的事情。

https://i.imgur.com/KWtbE1s.gifv

我正在使用 swing 库并使用大约每两毫秒滴答一次的计时器重新绘制。

编辑:

我修改了代码,使点相对于JPanel,但问题仍然存在。

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) {}
};

编辑 2:

不幸的是,我在这里创建的示例运行良好,但我不知道为什么它在真实代码中不起作用。我什至尝试将我在这个示例中所做的确切 class 复制到实际应用程序中,但问题仍然出现在真实代码中。

看来我将不得不对此进行更多研究。

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);
    }

}

查看 Moving Windows 是否有 class 可以为您执行此操作。

它比基本的拖动逻辑更复杂,因为它支持您可能不需要的其他功能。

在将侦听器添加到框架的一个(或多个)组件时拖动框架的基本逻辑(来自上面 class)是:

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);

编辑:

下面是一些更通用的代码,允许您通过指定父组件接收事件来直接或间接移动组件:

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();
            }
        });
*/
    }
}

尝试拖动 blue/red 个组件以查看差异。