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

Posted

技术标签:

【中文标题】JFrame从自定义按钮拖动不流畅【英文标题】:JFrame dragging from custom button not smooth 【发布时间】:2018-10-30 04:17:09 【问题描述】:

我使用与 Java - Custom shaped draggable JFrame 几乎相同的方法来移动 JFrame

我有一个扩展 JPanel 的类。在上述类中,我将之前的 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));

但是,当拖动窗口时,会有相当多的滞后或某种问题。这是一个视频,可以直观地展示发生了什么。

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

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

编辑:

我修改了代码,使这些点与 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:

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

看来我必须对此进行更多研究。

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


【问题讨论】:

“我使用几乎与 Java 相同的方法来移动 JFrame - 自定义形状的可拖动 JFrame” 为了尽快获得更好的帮助,请发布 minimal reproducible example 或 Short, Self Contained, Correct Example 您的代码。 @AndrewThompson 编辑可以接受吗? Is the edit acceptable? - 不。我们如何复制/粘贴/编译和测试该代码? 【参考方案1】:

查看Moving Windows 了解为您执行此操作的课程。

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

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

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

尝试拖动蓝色/红色组件以查看差异。

【讨论】:

感谢您的帮助!不幸的是,即使我提出了与 JFrame 相关的观点,问题似乎仍然存在。请参阅上述更新。 Please see the above update. - 将来在提问时,请发布正确的minimal reproducible example。说明问题。我们应该能够复制/粘贴/编译/测试您发布的代码。无论如何,我的第一个建议是一点点。我已经包含了上面链接中的简化代码。 我添加了示例,但示例本身可以正常工作。看来我必须深入研究一下。 我不知道为什么,但我只是使用了您的代码,并且问题以某种方式得到了解决。谢谢! @GigiBayte2,一个原因可能是代码更通用。它不依赖于外部变量。您应该始终尝试使代码可重用。请参阅编辑以获取更多可重用代码。

以上是关于JFrame从自定义按钮拖动不流畅的主要内容,如果未能解决你的问题,请参考以下文章

从自定义 uitableviewcell 内的按钮点击委托

从自定义按钮调用 Stripe 结帐表单

从自定义 UICollectionViewCell 获取正确的按钮

无法从自定义表格视图单元格按钮中删除行

Android:从自定义列表视图中单击的按钮获取列表视图项目

列表视图中的页脚按钮,如何从自定义列表适配器中获取值