2

I have a panel. I want to move the panel, inside a while loop, to the left and then to the right, after pressing a button and until a certain condition is met. But for this question let's say continuously. After it finishes an iteration, it doesn't move left or right. I used repaint() and Thread.sleep(1000) but nothing shows up. Please help me

while (true) {
    for (int i = 0; i < 5; i++) {
        jPanel1.setLocation(jPanel1.getLocation().x + 5, jPanel1.getLocation().y);
        i++;
        try {Thread.sleep(1000);} catch (InterruptedException ex) {}
        repaint();
    }

    for (int i = 0; i < 5; i++) {
        jPanel1.setLocation(jPanel1.getLocation().x - 5, jPanel1.getLocation().y);
        i++;
        try {Thread.sleep(1000);} catch (InterruptedException ex) {}
        repaint();
    }
}
Abra
  • 19,142
  • 7
  • 29
  • 41
GiGiK
  • 41
  • 4
  • 3
    Using Thread.sleep() in the gui thread will freeze the gui. – NomadMaker Sep 25 '20 at 12:05
  • 2
    Maybe use a [`Timer`](https://docs.oracle.com/javase/7/docs/api/javax/swing/Timer.html) – 001 Sep 25 '20 at 12:11
  • i tried to use a timer but i don't know how to make it work – GiGiK Sep 25 '20 at 12:23
  • 1
    See https://stackoverflow.com/questions/26688100/using-sleep-in-swing. – VGR Sep 25 '20 at 13:32
  • 1
    *i tried to use a timer but i don't know how to make it work* - and did you search the forum for examples? For example: https://stackoverflow.com/a/33907282/131872 or https://stackoverflow.com/a/7816604/131872 – camickr Sep 25 '20 at 14:38

1 Answers1

0

We meet again!

I managed to implement the moving JPanel using a Swing timer.

Since you want to move the JPanel, you need to place it in a parent container that is large enough to allow the JPanel to be placed in different locations within it. You also need to use a null layout manager because most layout managers will ignore calls to method setLocation().

More explanations after the code.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.WindowConstants;

public class MovePane implements ActionListener, Runnable {
    private static final String  START = "Start";
    private static final String  STOP = "Stop";

    private int  diff;
    private JButton  startButton;
    private JButton  stopButton;
    private JFrame  frame;
    private JPanel  jPanel1;
    private Timer  timer;

    public MovePane() {
        diff = 10;
    }

    @Override // jva.awt.event.ActionListener
    public void actionPerformed(ActionEvent event) {
        String actionCommand = event.getActionCommand();
        switch (actionCommand) {
            case START:
                launchTimer();
                break;
            case STOP:
                stopTimer();
                break;
            default:
                JOptionPane.showMessageDialog(frame,
                                              actionCommand,
                                              "Unhandled",
                                              JOptionPane.WARNING_MESSAGE);
        }
    }

    @Override // java.lang.Runnable
    public void run() {
        showGui();
    }

    private JButton createButton(String text, int mnemonic, String tooltip, boolean enabled) {
        JButton button = new JButton(text);
        button.setMnemonic(mnemonic);
        button.setToolTipText(tooltip);
        button.setEnabled(enabled);
        button.addActionListener(this);
        return button;
    }

    private JPanel createButtonsPanel() {
        JPanel buttonsPanel = new JPanel();
        startButton = createButton(START, KeyEvent.VK_M, "Sets panel in motion.", true);
        buttonsPanel.add(startButton);
        stopButton = createButton(STOP, KeyEvent.VK_P, "Stops panel motion.", false);
        buttonsPanel.add(stopButton);
        return buttonsPanel;
    }

    private JPanel createMainPanel() {
        JPanel mainPanel = new JPanel(null);
        mainPanel.setPreferredSize(new Dimension(400, 400));
        jPanel1 = new JPanel();
        jPanel1.setBounds(10, 200, 50, 50);
        jPanel1.setBorder(BorderFactory.createLineBorder(Color.RED, 2, true));
        mainPanel.add(jPanel1);
        return mainPanel;
    }

    private void launchTimer() {
        if (timer == null) {
            timer = new Timer(0, e -> movePanel());
            timer.setDelay(500);
        }
        timer.start();
        startButton.setEnabled(false);
        stopButton.setEnabled(true);
    }

    private void stopTimer() {
        timer.stop();
        startButton.setEnabled(true);
        stopButton.setEnabled(false);
    }

    private void movePanel() {
        Point location = jPanel1.getLocation();
        if (location.x > 180) {
            diff = -10;
        }
        else if (location.x < 10) {
            diff = 10;
        }
        location.x += diff;
        jPanel1.setLocation(location);
    }

    private void showGui() {
        frame = new JFrame("Move");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(createMainPanel(), BorderLayout.CENTER);
        frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new MovePane());
    }
}

Swing is event driven. Events are added to a queue and then read off the queue and performed. Setting the location of a JPanel is an event. Calling events in a loop just floods the queue with events and some may be lost. That's why there is the Swing Timer class. In the above code, every half second I change the location of jPanel1.

Abra
  • 19,142
  • 7
  • 29
  • 41
  • *Calling revalidate() tells the parent container of jPanel1 to redraw its contained components.* - actually it invokes the layout manager, which will typically cause a repaint() to be invoked automatically, so it is not needed. You typically only revalidate a parent panel when components are added/removed to invoke the layout manager. Also Swing components are smart enough to repaint() themselves when a property of the component is changed. So in this cause the repaint() is not needed either, since invoking the setLocation() method on the panel will cause it to invoke repaint() on itself. – camickr Sep 25 '20 at 15:10
  • Edited as per @camickr [comment](https://stackoverflow.com/questions/64063791/java-move-gui-panel-while-loop/64066428#comment113291694_64066428) – Abra Sep 25 '20 at 15:15