drawingPanel color change with displacement

2019-01-29 11:18发布

问题:

I'm using JGrasp, and in drawingPanel, I'm trying to create a ball that changes colors as it moves across the screen. Right now, I've got:

for (int i = 10; i<=i; i++) {
    Color c = new Color(i*i, 0, 0);
    pen.setColor(c);

My full simplified code is:

import java.awt.*;
import java.util.*;
import java.awt.Color;

public class BallSample {
   public static final int SIZE = 30;
   public static final int HALF = SIZE / 2;

   public static void main(String[] args) {
      Scanner console = new Scanner(System.in);

      DrawingPanel panel = new DrawingPanel(1000, 1000);
      panel.setBackground(Color.CYAN);
      Graphics pen = panel.getGraphics();

      for (int i = 10; i<=i; i++) {
         Color c = new Color(i*i, 0, 0);
         pen.setColor(c);
         pen.fillOval(500 - HALF, 500 - HALF, SIZE, SIZE);

         double xDisplacement = 30 * Math.cos(30);
         double yDisplacement = 30 * Math.sin(30) * -1;
         double x = 500.0;
         double y = 500.0;

         for (int j = 1; j <= 100; j++) {
            x = x + xDisplacement;
            y = y + yDisplacement;
         if (x <= 0 || x >= 1000) {
            xDisplacement = xDisplacement * -1;
         }
         if (y <= 0 || y >= 1000) {
            yDisplacement = yDisplacement * -1;
         }
            pen.fillOval((int) x - HALF, (int) y - HALF, SIZE, SIZE);

            panel.sleep(50);
         }      
      }
   }
}

I hope that's simplified enough.

回答1:

After having some issues in the following code, I was able to create a color-changing ball example.

First of all, having panel.sleep(50); makes me think (and I confirm with the DrawingPanel class link you posted in the comments) could become dangerous, it would be better to use a Swing Timer instead, however I don't pretend on using or read all the class as it's too long, so in the example I post here I'll be using the Swing Timer approach.

Also, I'm not sure why calling new Color(int rgb) constructor with a random int (between 0 and 255) doesn't update or create a new Color with those settings, then after reading this answer and another one (I lost its link, sorry), you can use new Color(float r, float g, float b), so you may use it with a random number up to 256.

You can also base your approach to a MVC pattern, having a Model Ball class which contains the coords and the Color it will have, a View JPanel that will draw the ball over time and a Controller that will change the ball's coords and color overtime as well.

Disclaimer: This example just moves the ball horizontally and doesn't validate whether it's on the screen or not, you need to adapt this example to your own needs.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Random;

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

public class ChangingColorBall {
    private JFrame frame;
    private Timer timer;
    private BallPane ballPane;
    private Ball ball;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new ChangingColorBall()::createAndShowGui); //We place our program on the EDT
    }

    private void createAndShowGui() {
        frame = new JFrame(getClass().getSimpleName());
        ball = new Ball();
        ballPane = new BallPane(ball);

        timer = new Timer(100, e -> { //This Timer will execute every 100ms and will increase ball's X coord and paint it again.
            ball.increaseX(); //This method increases X coord and changes ball's color on the model
            ballPane.revalidate();
            ballPane.repaint();
        });

        frame.add(ballPane);

        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        timer.start(); //We start the timer
    }

    class Ball {
        private int xCoord;
        private static final int Y = 50;
        private static final int SIZE = 20;
        private Color color;
        private Random r;

        public void increaseX() {
            xCoord += 10; //Increases X coord
            r = new Random();
            color = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256)); //Generates a new random color
        }

        public int getXCoord() {
            return xCoord;
        }

        public void setXCoord(int xCoord) {
            this.xCoord = xCoord;
        }

        public Color getColor() {
            return color;
        }

        public void setColor(Color color) {
            this.color = color;
        }
    }

    @SuppressWarnings("serial")
    class BallPane extends JPanel {
        private Ball ball;

        public BallPane(Ball ball) {
            this.ball = ball;
        }

        @Override
        protected void paintComponent(Graphics g) { //This method paints the ball according to the color it has and the actual coords it has
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;

            g2d.setColor(ball.getColor());
            g2d.fillOval(ball.getXCoord(), Ball.Y, Ball.SIZE, Ball.SIZE);
        }

        @Override
        public Dimension getPreferredSize() { //We should not call `.setSize(...)` method, so we override this one and call `.pack();` 
            return new Dimension(200, 100);
        }
    }
}

Note

Be aware to not call your coords as x or y or at least don't call their getters and setters as getX() or getY() or you could run into the issue I linked at the beginning of this answer.

Here's an example of how the GUI looks like, I can't do GIFs in my computer but it's better if you copy-paste the code and see how it works.

For a deeper understanding on how custom painting works in Swing, check Oracle's Lesson: Performing Custom Painting and Painting in AWT and Swing tutorials.