Java color changing Graphics with Timer

2019-05-24 16:57发布

问题:

I want to draw a disc that changes colors twice a second. The disk is drawn on a DrawPanel which extends a JPanel and in the main method the DrawPanel is added to a frame. For the colorchanging I use a timer which works when I'm trying to change the background of the DrawPanel in the main method (what i commented out).

Can someone tell me why it doesn't work for the Graphics g object or any other suggestions?

I just copied the code from the main method and added it into the paintComponent() method, but here it doesn't work.

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class DrawPanel extends JPanel{

    public GridBagLayout gbl;

    //position and dimension
    int x = 0, y = 0, width = 200, height = 200;

    public DrawPanel(){
        repaint();
    }

    public DrawPanel(GridBagLayout gridBagLayout) {
        this.gbl = gridBagLayout;
    }

    public void paintComponent(Graphics g){
        //Overwriting of old picture
        super.paintComponent(g);

        ActionListener action = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Random gen = new Random();

                Color color = new Color(gen.nextInt(256), gen.nextInt(256), gen.nextInt(256));

                //Draw color disk
                g.setColor(color);
                g.fillArc(x, y, width, height, 0, 360);
            }
        };

        Timer t = new Timer(500, action);
        t.setRepeats(true);
        t.setInitialDelay(0);
        t.start();



        //Draw boundary of circle
        g.setColor(Color.BLACK);
        g.drawArc(x, y, width, height, 0, 360);
    }


    public static void main(String[] args) {
        final JFrame frame = new JFrame();
        frame.setSize(300, 300);
        final DrawPanel panel = new DrawPanel();
            panel.setOpaque(true);
            frame.getContentPane().add(panel);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);

//      ActionListener action = new ActionListener() {
//          @Override
//          public void actionPerformed(ActionEvent e) {
//              Random gen = new Random();
//                  Color color = new Color(gen.nextInt(256), gen.nextInt(256), gen.nextInt(256));
//                  panel.setBackground(color);         
//          }
//      };
//
//      Timer t = new Timer(500, action);
//      t.setRepeats(true);
//      t.setInitialDelay(0);
//      t.start();

    }

}

回答1:

The Graphics object is transient, so you should not cache it even if the compiler allows that. Instead establish the timer in the constructor of the class, set the BG of the panel, then call for a repaint. E.G.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;

public class DrawPanel extends JPanel {

    Random gen = new Random();
    //position and dimension
    int x = 0, y = 0, width = 200, height = 200;
    Color drawColor = Color.BLACK;

    public DrawPanel() {
        repaint();
        ActionListener action = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Color color = new Color(gen.nextInt(256), gen.nextInt(256), gen.nextInt(256));

                //Draw color disk
                drawColor = color;
                DrawPanel.this.repaint();
            }
        };

        Timer t = new Timer(500, action);
        t.setRepeats(true);
        t.setInitialDelay(0);
        t.start();
    }

    @Override
    public void paintComponent(Graphics g) {
        //Overwriting of old picture
        super.paintComponent(g);

        //Draw boundary of circle
        g.setColor(drawColor);
        g.drawArc(x, y, width, height, 0, 360);
    }

    public static void main(String[] args) {
        final JFrame frame = new JFrame();
        frame.setSize(300, 300);
        final DrawPanel panel = new DrawPanel();
        panel.setOpaque(true);
        frame.getContentPane().add(panel);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}


回答2:

Graphics objects are only valid for that one draw

It would be better to instead to tell the JPanel to change it's current color (with a variable) and then tell it to repaint

  1. Add variable discColor to your JPanel

  2. Change your drawing code to not use timers and instead make it simple, just draw the disc based off of discColor

  3. With a timer, update the discColor variable and then call the panel's repaint() method