javabuttonjframemouselistener

Move button on JFrame Java


I have a task to make an application which will do the following:

So the problem is that when I do mouse click - it's fine, button moves to coordinates of click, but when I start moving mouse the button comes back to the original position.

    public class Window extends JFrame {
        private JLabel statusBar;
        private JPanel mainPanel, statusBarPanel;
        JButton button;
        public Window()
        {
            super("Window");
            setSize(400,600);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            mainPanel=new JPanel();
            statusBarPanel = new JPanel();
            statusBar=new JLabel("Coords: ");
            add(statusBarPanel, BorderLayout.SOUTH);
            add(mainPanel,BorderLayout.CENTER);
            mainPanel.setBorder(new BevelBorder(BevelBorder.LOWERED));
            statusBarPanel.add(statusBar,BorderLayout.CENTER);
            button = new JButton("Default text");
            mainPanel.add(button);
            MyMouseListener myMouseListener=new MyMouseListener();
            mainPanel.addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseMoved(MouseEvent e) {
                    super.mouseMoved(e);
                    statusBar.setText("Coords: ("+e.getX()+":"+e.getY()+")");
                }
            });
            mainPanel.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    super.mouseClicked(e);
                    button.setLocation(e.getX()-button.getWidth()/2,e.getY()-button.getHeight()/2);
                }
            });
            mainPanel.setFocusable(true);
            setVisible(true);
        }
    }

Solution

  • This is one of the rare cases where you don't want your panel to have a layout manager, since you need absolute positioning.

    JPanel has a default layout manager which is a FlowLayout, and your call to setLocation will only have a temporary effect until the panel revalidates its content and places things where they were supposed to be initially.

    See the following example with comments, it should give you the general idea :

    public class Window extends JFrame {
        private final JLabel statusBar;
        private final JPanel mainPanel, statusBarPanel;
        JButton button;
    
        public Window() {
            super("Window");
            setSize(400, 600);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            mainPanel = new JPanel();
            mainPanel.setLayout(null);// no layout for absolute positioning
            statusBarPanel = new JPanel();
            statusBar = new JLabel("Coords: ");
            add(statusBarPanel, BorderLayout.SOUTH);
            add(mainPanel, BorderLayout.CENTER);
            mainPanel.setBorder(new BevelBorder(BevelBorder.LOWERED));
            statusBarPanel.add(statusBar, BorderLayout.CENTER);
            button = new JButton("Default text");
    
            // place the button "manually"
            button.setBounds((int) (400 - button.getPreferredSize().getWidth()) / 2, 0,
                    (int) button.getPreferredSize().getWidth(),
                    (int) button.getPreferredSize().getHeight());
            mainPanel.add(button);
    
            mainPanel.addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseMoved(final MouseEvent e) {
                    super.mouseMoved(e);
                    statusBar.setText("Coords: (" + e.getX() + ":" + e.getY() + ")");
                }
            });
            mainPanel.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(final MouseEvent e) {
                    super.mouseClicked(e);
                    button.setLocation((int) (e.getX() - button.getPreferredSize().getWidth() / 2),
                            (int) (e.getY() - button.getPreferredSize().getHeight() / 2));
    
    
                }
            });
            mainPanel.setFocusable(true);
            setVisible(true);
        }
    
    }