Search code examples
javaswingpositionswingworker

Moving Component With SwingWorker


I'm currently trying to get a JLabel to move across the screen using a SwingWorker. I can get the JLabel to update the text in the SwingWoker but I can't make it change its position.

Here is what I have currently tried

import javax.swing.*;
import java.awt.*;
import java.util.List;

public class MainFrame
{
    public MainFrame()
    {
        addComponents();
    }

    public void addComponents()
    {
        JFrame mainFrame = new JFrame("Some Frame");
        JLabel enemyLabel = new JLabel("Enemy");
        mainFrame.add(enemyLabel);

        moveEnemies(enemyLabel, mainFrame);

        mainFrame.setLocationRelativeTo(null);
        mainFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);
        mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        mainFrame.pack();
        mainFrame.setVisible(true);
    }

    public void moveEnemies(JLabel label, JFrame frame)
    {
        SwingWorker<Void, Integer> enemyMover = new SwingWorker<Void, Integer>()
        {
            @Override
            protected Void doInBackground() throws Exception
            {
                for (int i = 0; i <= 5; i++)
                {
                    int x = 50 * i;
                    Thread.sleep(1000);
                    publish(x);
                }

                return null;
            }

            @Override
            protected void process(List<Integer> chunks)
            {
                label.setSize(50, 50);
                for(int num : chunks)
                {
                    label.setText(String.valueOf(num));
                    label.setLocation(num, 250);
                }
            }

            @Override
            protected void done()
            {
                label.setText("Finished");
            }
        };
        enemyMover.execute();
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(MainFrame::new);
    }
}

I've also printed out the JLabel.getLocation() and it prints the expected position, [250, 250] at the end of the loop, but the position doesn't change on the screen. I've also tried to revalidate(), & repaint() but neither seem to work.


Solution

  • You're fighting the frame's default layout manager (BorderLayout) which would very much like to keep the label where it thinks it should be.

    Take a look at Laying Out Components Within a Container

    To "fix" the "immediate" problem, you "could" set the layout manager for the frame to null, but this will open a can of very vicious and nasty worms. A better solution would be to stop using components in this way and use custom painting to render the text within a component.

    Take a look at Painting in AWT and Swing, Performing Custom Painting and 2D Graphics for more details.

    While it's possible to do some interesting animation with Swing components, there's a lot to take into consideration. If you're just creating a game of some kind, then it's better to use a custom painting based approach