为什么星星自转一段时间后会自动减慢

Why does the star rotation slows down automatically after some time

我有一个程序,我必须使用内部定时器事件以圆周运动旋转一颗星星。框架中有一个按钮可以改变星星的方向,还有一个滑块可以改变星星旋转的速度。

这是我的主Class

import javax.swing.JFrame;

public class Main {
    public static void main(String args[]) {
//      Making an instance of the class that makes the frame
        MainFrame frame = new MainFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500, 500);
        frame.setVisible(true);
    }
}

构成框架的MainFrame Class。

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;


@SuppressWarnings("serial")
public class MainFrame extends JFrame{
    private StarGraphics frameStar;
    private JButton starToggle;
    public JSlider starSpeed;
    
    
    public MainFrame() {
//      Setting some properties of the frame
        setTitle("Star Moving with Internal Events");
        setLayout(new BorderLayout());
        
//      Initializing the panel which has rotating star
        frameStar = new StarPainter(this);
        
//      Adding the bottom toggle button and slider
        addToggler();   
    }
    
//  Getter for the slider value
    public int sliderSpeed() {
        return starSpeed.getValue();
    }
    
    
//  Adds Button to change the direction of the star
    private void addToggler() {
//      Adding another jpanel which has layout set to null so i can add button of my size
        JPanel panel = new JPanel();
        panel.setLayout(null);
        panel.setPreferredSize(new Dimension(20,80));
        panel.setBackground(Color.WHITE);
        
//      Initializing button and its action listener to change star direction
        starToggle = new JButton("Toggle");
        starToggle.setBounds(190, 0, 80, 20);
        starToggle.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if(e.getSource() == starToggle) {
                    frameStar.ChangeDirections();
                }
            }
        });
        
//      Adding button to panel
        panel.add(starToggle);
        
//      Adding slider to the panel
        addSlider(panel);
        
//      Adding panel to the main Panel at the bottom
        add(panel, BorderLayout.SOUTH);
    }
    
    private void addSlider(JPanel panel) {
//      Adding Slider and it's properties
        starSpeed = new JSlider(JSlider.HORIZONTAL, 0, 20, 5);
        starSpeed.setMajorTickSpacing(10);
        starSpeed.setMinorTickSpacing(1);
        starSpeed.setPaintTicks(true);
        starSpeed.setPaintLabels(true);
        starSpeed.setBounds(70,30,400, 45);
        
//      Adding Slider-ChangeListener to change the rotation speed of the star
        starSpeed.addChangeListener(new ChangeListener() { // anonymous inner class  
            // handle change in slider value
            @Override
            public void stateChanged(ChangeEvent e) {
                frameStar.ChangeSpeed(starSpeed.getValue());
            }
         } 
      );
        
//      Adding label besides the slider
        JLabel label = new JLabel("Speed : ");
        label.setBounds(10 , 10, 80, 80);
        panel.add(label);
        panel.add(starSpeed);
    }
}

Class 创建有星形旋转的面板

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

@SuppressWarnings("serial")
public class StarGraphics extends JPanel{
    private boolean toggleDir = false;
    private int speed = 10;
    private Timer timer;
    protected double angleOfStarRotation = 0;
    
    public StarGraphics(JFrame frame) {
        
        setPreferredSize(new Dimension(500, 470));
        setBackground(Color.BLACK);
        setLayout(new BorderLayout());
        frame.add(this, BorderLayout.CENTER);
        
        startTimer();
    }
    
    public void startTimer() {
        timer = new Timer(speed, new ActionListener() {
            public void actionPerformed(ActionEvent e){
//              System.out.println(angleOfStarRotation);
                if(!toggleDir)  //rotates clockwise
                    angleOfStarRotation = angleOfStarRotation + 1;
                else        //rotates counterclockwise
                    angleOfStarRotation = angleOfStarRotation - 1;
                  
//                if (angleOfStarRotation == 360 || angleOfStarRotation == -360)  // If there is a full circle, it will reset the angle to zero
//                    angleOfStarRotation = 0;
                  
                repaint();
            }});
        timer.start();
    }
    
    public void ChangeSpeed(int newSpeed) { 
        this.speed = newSpeed;
        timer.setDelay(speed);
    }   
    public void ChangeDirections() {toggleDir = !toggleDir; }

}

还有一个 class 将星星绘制到面板中

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.GeneralPath;
import java.security.SecureRandom;

import javax.swing.JFrame;

@SuppressWarnings("serial")
public class StarPainter extends StarGraphics{
    private int[] starXPoints = {55, 67, 109, 73, 83, 55, 27, 37, 1, 43};
    private int[] starYPoints = {0, 36, 36, 54, 96, 72, 96, 54, 36, 36};
    GeneralPath starDesign = new GeneralPath();
    
    public StarPainter(JFrame frame) {
        super(frame);
    }
    
    public void drawStar(GeneralPath path) {
        path.moveTo(starXPoints[0], starYPoints[0]);
        
        for(int i=0; i<10; i++)
           path.lineTo(starXPoints[i], starYPoints[i]);
        
        path.closePath();
    }
    
    public void starActions(Graphics2D g) {
        int startAngle = 360;
        // For Random Color
        SecureRandom random = new SecureRandom(); 
           
        // rotate around origin and draw stars in random colors
        for (int count = 1; count <= 1; count++) 
        {
            double angle = startAngle - 90;
            // rotate coordinate system
            g.rotate(angleOfStarRotation * Math.PI / angle); //rotates as per the rotated angle    // 
           // set random drawing color
            g.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)));
          // draw filled star
            g.fill(starDesign);
          // dispose the star
            g.dispose();
        }
    }
    
    @Override
    public void paintComponent(Graphics g)
    {
       super.paintComponent(g);
       
       Graphics2D g2d = (Graphics2D) g;
      
       drawStar(starDesign);
       
       g2d.translate(250,150);
       
       starActions(g2d);
       
    }
}

After running the code, the output will show a frame with coloring star rotating around the panel in circular motion, but after 2 full rotation, the star rotation slow down automatically. Does anyone know why that happens?

你的“核心”问题就在这里...

public void drawStar(GeneralPath path) {
    path.moveTo(starXPoints[0], starYPoints[0]);
    
    for(int i=0; i<10; i++)
       path.lineTo(starXPoints[i], starYPoints[i]);
    
    path.closePath();
}

每次您 re-draw 组件时都会调用此方法,这意味着您正在向形状添加新点,使其在每次绘制过程中变得无限复杂。

相反,只需在构造函数中创建形状...

public StarPainter() {
    starDesign.moveTo(starXPoints[0], starYPoints[0]);

    for (int i = 0; i < 10; i++) {
        starDesign.lineTo(starXPoints[i], starYPoints[i]);
    }

    starDesign.closePath();
}

正如已经指出的那样,您正在处理一个您没有创建的 Graphics 上下文。

如果您要更改上下文的转换,您应该始终创建自己的副本,例如...

public void starActions(Graphics2D g) {
    int startAngle = 360;
    // For Random Color
    SecureRandom random = new SecureRandom();

    g = (Graphics2D) g.create();
    // rotate around origin and draw stars in random colors
    //for (int count = 1; count <= 1; count++) {
        double angle = startAngle - 90;
        // rotate coordinate system
        g.rotate(angleOfStarRotation * Math.PI / angle); //rotates as per the rotated angle    // 
        // set random drawing color
        g.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)));
        // draw filled star
        g.fill(starDesign);
        // dispose the star
        g.dispose();
    //}
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);

    Graphics2D g2d = (Graphics2D) g.create();
    g2d.translate(250, 150);
    starActions(g2d);
    g2d.dispose();

}

此外,将 JFrame 的引用传递给组件...

public StarGraphics(JFrame frame) {
    
    setPreferredSize(new Dimension(500, 470));
    setBackground(Color.BLACK);
    setLayout(new BorderLayout());
    frame.add(this, BorderLayout.CENTER);
    
    startTimer();
}

是个坏主意。组件不需要,也不是它的责任,与框架交互。您只是不必要地公开了实现细节。

可运行示例

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.GeneralPath;
import java.security.SecureRandom;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                MainFrame frame = new MainFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(500, 500);
                frame.setVisible(true);
            }
        });
    }

    public class MainFrame extends JFrame {

        private StarGraphics frameStar;
        private JButton starToggle;
        public JSlider starSpeed;

        public MainFrame() {
            //      Setting some properties of the frame
            setTitle("Star Moving with Internal Events");
            setLayout(new BorderLayout());

            //      Initializing the panel which has rotating star
            frameStar = new StarPainter();
            add(frameStar);

            //      Adding the bottom toggle button and slider
            addToggler();
        }

        //  Getter for the slider value
        public int sliderSpeed() {
            return starSpeed.getValue();
        }

        //  Adds Button to change the direction of the star
        private void addToggler() {
            //      Adding another jpanel which has layout set to null so i can add button of my size
            JPanel panel = new JPanel();
            panel.setLayout(null);
            panel.setPreferredSize(new Dimension(20, 80));
            panel.setBackground(Color.WHITE);

            //      Initializing button and its action listener to change star direction
            starToggle = new JButton("Toggle");
            starToggle.setBounds(190, 0, 80, 20);
            starToggle.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (e.getSource() == starToggle) {
                        frameStar.ChangeDirections();
                    }
                }
            });

            //      Adding button to panel
            panel.add(starToggle);

            //      Adding slider to the panel
            addSlider(panel);

            //      Adding panel to the main Panel at the bottom
            add(panel, BorderLayout.SOUTH);
        }

        private void addSlider(JPanel panel) {
            //      Adding Slider and it's properties
            starSpeed = new JSlider(JSlider.HORIZONTAL, 0, 20, 5);
            starSpeed.setMajorTickSpacing(10);
            starSpeed.setMinorTickSpacing(1);
            starSpeed.setPaintTicks(true);
            starSpeed.setPaintLabels(true);
            starSpeed.setBounds(70, 30, 400, 45);

            //      Adding Slider-ChangeListener to change the rotation speed of the star
            starSpeed.addChangeListener(new ChangeListener() { // anonymous inner class  
                // handle change in slider value
                @Override
                public void stateChanged(ChangeEvent e) {
                    frameStar.ChangeSpeed(starSpeed.getValue());
                }
            }
            );

            //      Adding label besides the slider
            JLabel label = new JLabel("Speed : ");
            label.setBounds(10, 10, 80, 80);
            panel.add(label);
            panel.add(starSpeed);
        }
    }

    public class StarGraphics extends JPanel {

        private boolean toggleDir = false;
        private int speed = 10;
        private Timer timer;
        protected double angleOfStarRotation = 0;

        public StarGraphics() {
            setPreferredSize(new Dimension(500, 470));
            setBackground(Color.BLACK);
            setLayout(new BorderLayout());
            startTimer();
        }

        public void startTimer() {
            timer = new Timer(speed, new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    //              System.out.println(angleOfStarRotation);
                    if (!toggleDir) //rotates clockwise
                    {
                        angleOfStarRotation = angleOfStarRotation + 1;
                    } else //rotates counterclockwise
                    {
                        angleOfStarRotation = angleOfStarRotation - 1;
                    }

                    System.out.println("tick");
                    //                if (angleOfStarRotation == 360 || angleOfStarRotation == -360)  // If there is a full circle, it will reset the angle to zero
                    //                    angleOfStarRotation = 0;
                    repaint();
                }
            });
            timer.start();
        }

        public void ChangeSpeed(int newSpeed) {
            this.speed = newSpeed;
            timer.setDelay(speed);
        }

        public void ChangeDirections() {
            toggleDir = !toggleDir;
        }

    }

    public class StarPainter extends StarGraphics {

        private int[] starXPoints = {55, 67, 109, 73, 83, 55, 27, 37, 1, 43};
        private int[] starYPoints = {0, 36, 36, 54, 96, 72, 96, 54, 36, 36};
        GeneralPath starDesign = new GeneralPath();

        public StarPainter() {
            starDesign.moveTo(starXPoints[0], starYPoints[0]);

            for (int i = 0; i < 10; i++) {
                starDesign.lineTo(starXPoints[i], starYPoints[i]);
            }

            starDesign.closePath();
        }

        public void starActions(Graphics2D g) {
            int startAngle = 360;
            // For Random Color
            SecureRandom random = new SecureRandom();

            g = (Graphics2D) g.create();
            // rotate around origin and draw stars in random colors
            //for (int count = 1; count <= 1; count++) {
            double angle = startAngle - 90;
            // rotate coordinate system
            g.rotate(angleOfStarRotation * Math.PI / angle); //rotates as per the rotated angle    // 
            // set random drawing color
            g.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)));
            // draw filled star
            g.fill(starDesign);
            // dispose the star
            g.dispose();
            //}
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.translate(250, 150);
            starActions(g2d);
            g2d.dispose();

        }
    }
}