简体   繁体   中英

Using repaint() to draw on JPanel which is added to JFrame

Attempting to draw my rectangle across the screen horizontally in realtime. When I run this I get nothing but the JFrame. I'm not sure what I am missing aside from maybe some type of threading freeze to redraw the shape maybe?

public class ScreenTest extends JFrame {
    
    int rectY = 50;
    
    public ScreenTest()
    {
        setSize(300,200);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
        
    }
    
    private class DrawPanel extends JPanel {
        
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.GREEN);
            g.fillRect(80, rectY, 50, 50);
       }
    }
    
    public void Draw()
    {
        DrawPanel test = new DrawPanel();
        add(test);
        
        while (rectY < 200)
        {
            rectY = rectY + 10;
            test.repaint();
        }

    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ScreenTest myWindow = new ScreenTest(); 
        myWindow.Draw();
    }
    
}

Swing is single threaded and not thread safe.

What this means is, you should not perform any kind of long running or blocking operations within the "Event Dispatching Thread", as this will stop the UI from been painted or responding to new events.

It also means you should not update the UI, or any state the UI relies on, from outside the context of the Event Dispatching Thread.

Your code "is" working, but because the while-loop can run so fast, it's completing before the window is realised on the screen (visible and updatable). Swing is also optimised, so all the repaint calls are likely been consolidated into a single repaint pass.

A better solution might to start with Swing `Timer, which acts as a pseudo repeating loop, but which is called on within the context of the Event Dispatching Thread.

Start by taking a look at Concurrency in Swing andHow to Use Swing Timers for more details.

Runnable Example

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class ScreenTest extends JFrame {

    public ScreenTest() {
        setSize(300, 200);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

    }

    private class DrawPanel extends JPanel {

        int rectY = 50;
        private Timer timer;

        // This is just convince 
        @Override
        public void addNotify() {
            super.addNotify();
            timer = new Timer(25, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    rectY += 1;
                    repaint();
                }
            });
            // Otherwise it disappears to fast
            timer.setInitialDelay(1000);
            timer.start();
        }

        @Override
        public void removeNotify() {
            super.removeNotify();
            timer.stop();
            timer = null;
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.GREEN);
            g.fillRect(80, rectY, 50, 50);
        }
    }

    public void Draw() {
        DrawPanel test = new DrawPanel();
        add(test);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                ScreenTest myWindow = new ScreenTest();
                myWindow.Draw();
            }
        });
    }

}

it is working but is so fast that you can't see it, you need to make the loop which changes the Y coordinate slower with a delay. to solve it i used Thread.sleep() in the while loop:

package paquete;

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

public class ScreenTest extends JFrame {

    int rectY = 50;

    public ScreenTest()
    {
        setSize(300,200);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

    }

    private class DrawPanel extends JPanel {

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.GREEN);
            g.fillRect(80, rectY, 50, 50);
        }
    }

    public void Draw() throws InterruptedException {
        DrawPanel test = new DrawPanel();
        add(test);

        while (rectY < 200)
        {
            rectY = rectY + 10;
            Thread.sleep(100);
            test.repaint();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        ScreenTest myWindow = new ScreenTest();
        myWindow.Draw();
    }

}

i hope this helps you, you can change the duration changing the number inside the argument of Thread.sleep()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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