简体   繁体   English

从另一个类运行时,GUI不响应鼠标单击和拖动

[英]GUI doesn't respond to mouse click and drag while running from another class

I have 2 classes: TestingPanel and SnipIt . 我有2个类: TestingPanelSnipIt

SnipIt is used for selecting an area on the screen. SnipIt用于选择屏幕上的区域。

TestingPanel is the main frame, containing a button to run method Snip() and receiving the return values. TestingPanel是主框架,包含一个按钮来运行方法Snip()并接收返回值。

If I test the SnipIt class separately, it works. 如果我单独测试SnipIt类,它可以工作。 But if I create a SnipIt object and run method Snip() from TestingPanel class, it doesn't work. 但是如果我创建一个SnipIt对象并从TestingPanel类运行方法Snip(),它就不起作用。 The GUI just freezes and doesn't respond to mouse click or drag event. GUI只是冻结并且不响应鼠标单击或拖动事件。 I guess something block the thread handle the mouse events but I'm not sure. 我想有些东西会阻止线程处理鼠标事件,但我不确定。

I have been stuck for couple hours and still don't know what caused the issue. 我被困了几个小时仍然不知道是什么导致了这个问题。 Please help. 请帮忙。

TestingPanel TestingPanel

package Testing;

import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class TestingPanel {

    private JFrame frame;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    TestingPanel window = new TestingPanel();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public TestingPanel() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 200, 160);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(null);

        JButton btnSnip = new JButton("Snip");
        btnSnip.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                getSelectionSize();
            }
        });
        btnSnip.setBounds(47, 87, 89, 23);
        frame.getContentPane().add(btnSnip);
    }

    private void getSelectionSize() {
        int[] size = new int[4];

        Thread worker = new Thread(new Runnable() {
            public void run() {
                SnipIt sn = new SnipIt();
                sn.snip();

                while(!sn.complete) {
                    try {
                        Thread.sleep(800);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                size[0] = sn.returnSize()[0];
                size[1] = sn.returnSize()[1];
                size[2] = sn.returnSize()[2];
                size[3] = sn.returnSize()[3];
            }
        });

        worker.start();

        try {
            worker.join();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }

        System.out.println(size[0] + " " + size[1] + " " + size[2] + " " + size[3]);
    }
}

SnipIt SnipIt

package Testing;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class SnipIt {

    private int recX = 0;
    private int recY = 0;
    private int recWidth = 0;
    private int recHeight = 0;
    public boolean complete = false;

    /*
    public static void main(String [] args)
    {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                SnipIt s = new SnipIt();
                s.snip();
            }
        });
    }
    */

    public void snip() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ex) {
        }

        JFrame frame = new JFrame();
        frame.setUndecorated(true);
        frame.setBackground(new Color(0, 0, 0, 0));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        frame.add(new SnipItPane());
        frame.setBounds(getVirtualBounds());
        frame.setVisible(true);
    }

    @SuppressWarnings("serial")
    public class SnipItPane extends JPanel {

        private Point mouseAnchor;
        private Point dragPoint;

        private SelectionPane selectionPane;
        private ControlPane controlPane;

        public SnipItPane() {
            setOpaque(false);
            setLayout(null);
            selectionPane = new SelectionPane();
            controlPane   = new ControlPane();
            add(selectionPane);
            add(controlPane);
            MouseAdapter adapter = new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    mouseAnchor = e.getPoint();
                    dragPoint = null;
                    selectionPane.setLocation(mouseAnchor);
                    selectionPane.setSize(0, 0);
                    controlPane.setLocation(mouseAnchor);
                    controlPane.setSize(0, 0);
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    dragPoint = e.getPoint();
                    int width = dragPoint.x - mouseAnchor.x;
                    int height = dragPoint.y - mouseAnchor.y;

                    int x = mouseAnchor.x;
                    int y = mouseAnchor.y;

                    if (width < 0) {
                        x = dragPoint.x;
                        width *= -1;
                    }
                    if (height < 0) {
                        y = dragPoint.y;
                        height *= -1;
                    }
                    selectionPane.setBounds(x, y, width, height);
                    selectionPane.revalidate();
                    int controlY = y + height + 5;
                    controlPane.setBounds(x, controlY, width, 25);
                    controlPane.revalidate();
                    repaint();
                }
            };
            addMouseListener(adapter);
            addMouseMotionListener(adapter);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();

            Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
            Area area = new Area(bounds);
            area.subtract(new Area(selectionPane.getBounds()));

            g2d.setColor(new Color(102, 102, 102, 80));
            g2d.fill(area);

        }
    }

    @SuppressWarnings("serial")
    public class ControlPane extends JPanel {
        private JButton btnClose;

        public ControlPane() {
            setOpaque(false);
            btnClose = new JButton("Save");
            setLayout(new BorderLayout());
            this.add(btnClose, BorderLayout.NORTH);

            btnClose.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    complete = true;
                    SwingUtilities.getWindowAncestor(ControlPane.this).dispose();
                }
            });
        }
    }


    @SuppressWarnings("serial")
    public class SelectionPane extends JPanel {

        public SelectionPane() {
            setOpaque(false);

            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e) {
                    recX = getX();
                    recY = getY();
                    recWidth = getWidth();
                    recHeight = getHeight();
                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            float strokeWidth = 1.0f;
            float dash1[] = {10.0f};
            BasicStroke dashed =
                    new BasicStroke(strokeWidth,
                    BasicStroke.CAP_BUTT,
                    BasicStroke.JOIN_MITER,
                    10.0f, dash1, 0.0f);
            g2d.setColor(Color.BLACK);
            g2d.setStroke(dashed);
            g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
            g2d.dispose();
        }
    }

    public static Rectangle getVirtualBounds() {
        Rectangle bounds = new Rectangle(0, 0, 0, 0);

        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice lstGDs[] = ge.getScreenDevices();
        for (GraphicsDevice gd : lstGDs) {
            bounds.add(gd.getDefaultConfiguration().getBounds());
        }
        return bounds;
    }

    public int[] returnSize() {
        int[] size = new int[4];
        size[0] = recX;
        size[1] = recY;
        size[2] = recWidth;
        size[3] = recHeight;
        return size;
    }
}

You're running the Snipit application in a background thread and then freezing that thread with Thread.sleep and a while true block, something guaranteed to freeze the GUI. 您正在后台线程中运行Snipit应用程序,然后使用Thread.sleep和while true块冻结该线程,保证冻结GUI。 Read Lesson: Concurrency in Swing and then be sure to always run Swing applications on the single Swing event thread, and do any long running or sleeping code in a background thread. 阅读课程:Swing中的并发性,然后确保始终在单个Swing事件线程上运行Swing应用程序,并在后台线程中执行任何长时间运行或休眠代码。

Possible solutions to your issue: 您的问题的可能解决方案:

  • Make the Snipit window an undecorated modal dialog. 使Snipit窗口成为未修饰的模态对话框。 This way program flow from the calling code stops when the dialog is visible and resumes when no longer visible. 这样,来自调用代码的程序流在对话框可见时停止,并在不再可见时恢复。
  • Or Make the Snipit window JFrame an instance field of the class and allow outside classes to add listeners to it so that they will be notified when it closes. 或者使Snipit窗口JFrame成为类的实例字段,并允许外部类向其添加侦听器,以便在关闭时通知它们。

eg, 例如,

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class TestSnipit {

    private static void createAndShowGui() {
        boolean runTest = true;

        if (runTest) {
            TestingPanel.main(null);
        } else {
            SnipIt.main(null);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

class TestingPanel {

    private JFrame frame;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    TestingPanel window = new TestingPanel();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public TestingPanel() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 200, 160);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(null);

        JButton btnSnip = new JButton("Snip");
        btnSnip.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                getSelectionSize();
            }
        });
        btnSnip.setBounds(47, 87, 89, 23);
        frame.getContentPane().add(btnSnip);
    }

    private void getSelectionSize() {
        int[] size = new int[4];

        // !!
        SnipIt sn = new SnipIt();
        sn.snip(frame);

        // Thread worker = new Thread(new Runnable() {
        // public void run() {
        // SnipIt sn = new SnipIt();
        // sn.snip();
        //
        // while (!sn.complete) {
        // try {
        // Thread.sleep(800);
        // } catch (InterruptedException e) {
        // e.printStackTrace();
        // }
        // }
        //

        size[0] = sn.returnSize()[0];
        size[1] = sn.returnSize()[1];
        size[2] = sn.returnSize()[2];
        size[3] = sn.returnSize()[3];

        // }
        // });
        //
        // worker.start();
        //
        // try {
        // worker.join();
        // } catch (InterruptedException e1) {
        // e1.printStackTrace();
        // }

        System.out.println(size[0] + " " + size[1] + " " + size[2] + " " + size[3]);
    }
}

class SnipIt {

    private int recX = 0;
    private int recY = 0;
    private int recWidth = 0;
    private int recHeight = 0;
    public boolean complete = false;

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                SnipIt s = new SnipIt();
                s.snip(null); // !!
            }
        });
    }

    public void snip(Window owner) { // !!
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ex) {
        }

        // JFrame frame = new JFrame();
        JDialog frame = new JDialog(owner, null, ModalityType.APPLICATION_MODAL); // !!
        frame.setUndecorated(true);
        frame.setBackground(new Color(0, 0, 0, 0));
        // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); // !!
        frame.setLayout(new BorderLayout());
        frame.add(new SnipItPane());
        frame.setBounds(getVirtualBounds());
        frame.setVisible(true);
    }

    @SuppressWarnings("serial")
    public class SnipItPane extends JPanel {

        private Point mouseAnchor;
        private Point dragPoint;

        private SelectionPane selectionPane;
        private ControlPane controlPane;

        public SnipItPane() {
            setOpaque(false);
            setLayout(null);
            selectionPane = new SelectionPane();
            controlPane = new ControlPane();
            add(selectionPane);
            add(controlPane);
            MouseAdapter adapter = new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    mouseAnchor = e.getPoint();
                    dragPoint = null;
                    selectionPane.setLocation(mouseAnchor);
                    selectionPane.setSize(0, 0);
                    controlPane.setLocation(mouseAnchor);
                    controlPane.setSize(0, 0);
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    dragPoint = e.getPoint();
                    int width = dragPoint.x - mouseAnchor.x;
                    int height = dragPoint.y - mouseAnchor.y;

                    int x = mouseAnchor.x;
                    int y = mouseAnchor.y;

                    if (width < 0) {
                        x = dragPoint.x;
                        width *= -1;
                    }
                    if (height < 0) {
                        y = dragPoint.y;
                        height *= -1;
                    }
                    selectionPane.setBounds(x, y, width, height);
                    selectionPane.revalidate();
                    int controlY = y + height + 5;
                    controlPane.setBounds(x, controlY, width, 25);
                    controlPane.revalidate();
                    repaint();
                }
            };
            addMouseListener(adapter);
            addMouseMotionListener(adapter);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();

            Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
            Area area = new Area(bounds);
            area.subtract(new Area(selectionPane.getBounds()));

            g2d.setColor(new Color(102, 102, 102, 80));
            g2d.fill(area);

        }
    }

    @SuppressWarnings("serial")
    public class ControlPane extends JPanel {
        private JButton btnClose;

        public ControlPane() {
            setOpaque(false);
            btnClose = new JButton("Save");
            setLayout(new BorderLayout());
            this.add(btnClose, BorderLayout.NORTH);

            btnClose.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    complete = true;
                    SwingUtilities.getWindowAncestor(ControlPane.this).dispose();
                }
            });
        }
    }

    @SuppressWarnings("serial")
    public class SelectionPane extends JPanel {

        public SelectionPane() {
            setOpaque(false);

            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e) {
                    recX = getX();
                    recY = getY();
                    recWidth = getWidth();
                    recHeight = getHeight();
                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            float strokeWidth = 1.0f;
            float dash1[] = { 10.0f };
            BasicStroke dashed = new BasicStroke(strokeWidth, BasicStroke.CAP_BUTT,
                    BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f);
            g2d.setColor(Color.BLACK);
            g2d.setStroke(dashed);
            g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
            g2d.dispose();
        }
    }

    public static Rectangle getVirtualBounds() {
        Rectangle bounds = new Rectangle(0, 0, 0, 0);

        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice lstGDs[] = ge.getScreenDevices();
        for (GraphicsDevice gd : lstGDs) {
            bounds.add(gd.getDefaultConfiguration().getBounds());
        }
        return bounds;
    }

    public int[] returnSize() {
        int[] size = new int[4];
        size[0] = recX;
        size[1] = recY;
        size[2] = recWidth;
        size[3] = recHeight;
        return size;
    }
}

A side issue unrelated to your initial problem is your use of null layougs. 与您的初始问题无关的一个侧面问题是您使用了无效的问题。 While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. 虽然null布局和setBounds()似乎可以像创建复杂GUI的最简单和最好的方式一样使用Swing GUI,但是你创建的Swing GUI越多,在使用它们时就会遇到更严重的困难。 They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one. 当GUI调整大小时,它们不会调整组件的大小,它们是增强或维护的皇室女巫,当它们被放置在滚动窗格中时它们完全失败,当它们在所有平台上观看时或者与原始平台不同的屏幕分辨率时它们看起来很糟糕。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM