一旦JComponent离开屏幕,它就会停止渲染

时间:2017-02-22 23:28:33

标签: java swing

我正在尝试制作一个简单的动画,其中一个矩形从屏幕开始(在屏幕右边缘的右侧)并向左移动。因此,在这种情况下,我的框架宽度为1000,墙壁的起始值为x值1100.显然,首先,矩形不应该是 我们可见。但是当矩形向左移动时,它最终应该变得可见。但是,此动画不会这样做。即使墙的x值在屏幕范围内,也不会被渲染。

我尝试在println()方法中添加paintComponent()语句,我发现paintComponent()没有被框架的repaint()方法调用。我认为当墙首次添加到框架时,Swing决定,因为它不在屏幕上,所以即使墙最终进入屏幕,Swing认为它不需要渲染。得到渲染

我尝试重新验证框架和组件并使其无效,但没有任何效果。请帮我解决这个问题。以下是代码:

package graphics.simpleAnimation;

public class Simple_Animation implements Runnable {

    private UI ui; // The UI (frame)

    private Wall wall; // Wall object that moves across the screen

    private Simple_Animation() {

        // Initialize the wall object (intentionally given an x value that is greater than the frame's width)
        wall = new Wall(1100, 400, 200, 400);

        // Initialize the UI (width is only 1000)
        ui = new UI(1000, 600, "Geometry Dash");

        // Add the wall to the ui (the frame)
        ui.add(wall);

    }

    public void run() {
        // Set the frame visible
        ui.setVisible(true);

        // Repaint the frame and move the wall
        while (true) {
            ui.repaint();
            wall.moveWall(-2, 0);

            try {
                Thread.sleep(16);
            } catch (InterruptedException IE) {
                System.out.println(IE);
            }

        }

    }

    // Starts the program in a new thread
    public static void main(String[] args) {
        Simple_Animation simpleAnimation = new Simple_Animation();
        new Thread(simpleAnimation).start();
    }

}


package graphics.simpleAnimation;

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

public class UI extends JFrame {

    // Variables storing the width and height of the content pane (where the components are being rendered)
    public int content_pane_width;
    public int content_pane_height;

    public UI(int frameW, int frameH, String frameTitle) {

        setTitle(frameTitle);
        setSize(frameW, frameH);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setLayout(null);

        content_pane_width = getContentPane().getWidth();
        content_pane_height = getContentPane().getHeight();

    }

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

}

package graphics.simpleAnimation;

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

public class Wall extends JComponent {

    private int wallX;
    private int wallY;
    private int wallW;
    private int wallH;


    Wall(int x, int y, int sizeX, int sizeY) {
        wallX = x;
        wallY = y;
        wallW = sizeX;
        wallH = sizeY;

        setSize(getPreferredSize());
    }

    public void moveWall(int moveX, int moveY) {
        wallX += moveX;
        wallY += moveY;
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(wallW, wallH);
    }

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;

        setLocation(wallX, wallY);
        g2d.fillRect(0, 0, wallW, wallH);
    }
}

2 个答案:

答案 0 :(得分:3)

我可以在您的程序中找到一些错误

  1. 您使用的是null layout,请参阅Null layout is evil以及this question中的答案,了解您应该避免使用的原因。(可能不是在这种情况下,根据@ MadProgrammer在下面的评论),这只是另一种方法

  2. while (true) {此行可能会阻止Event Dispatch Thread (EDT)以及此行:Thread.sleep(16);,请参阅Lesson: Concurrency in Swing以了解有关How to use Swing Timers的更多信息。您还应该将程序放在EDT上,这可以完成:

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                //Your constructor here
            }
        });
    }
    
  3. 您没有在super.paintComponent()类的paintComponent()方法上调用Wall,这可能会破坏画颜链,始终先调用它。

  4. 您正在扩展JComponent,最好扩展JPanel并使用Shape API对其进行自定义绘制

  5. 考虑到以上所有因素,您可以拥有类似这样的代码:

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.geom.Rectangle2D;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;
    
    public class SingleAnimation {
    
        private JFrame frame;
        private Timer timer;
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new SingleAnimation().createAndShowGui();
                }
            });
        }
    
        public void createAndShowGui() {
            frame = new JFrame(getClass().getSimpleName());
    
            Wall wall = new Wall(300, 0);
    
            timer = new Timer(16, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    wall.moveWall(-2, 0);
                }
            });
    
            timer.setInitialDelay(0);
            timer.start();
    
            frame.add(wall);
            frame.pack();
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
    }
    
    class Wall extends JPanel {
        private int xCoord;
        private int yCoord;
    
        public int getxCoord() {
            return xCoord;
        }
    
        public void setxCoord(int xCoord) {
            this.xCoord = xCoord;
        }
    
        public int getyCoord() {
            return yCoord;
        }
    
        public void setyCoord(int yCoord) {
            this.yCoord = yCoord;
        }
    
        public Wall(int x, int y) {
            this.xCoord = x;
            this.yCoord = y;
        }
    
        public void moveWall(int xUnits, int yUnits) {
            xCoord += xUnits;
            yCoord += yUnits;
            repaint();
        }
    
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(Color.BLUE);
    
            g2d.fill(new Rectangle2D.Double(xCoord, yCoord, 100, 20));
        }
    }
    

    这将产生类似这样的输出:

    enter image description here

答案 1 :(得分:2)

  • 不要在绘画方法中做setLocation(wallX, wallY);,绘画应该绘制当前状态永远不会改变它。
  • 您还要将自己的属性与组件属性混合使用,我建议只使用组件预先存在的属性,因为您自己尝试放置/调整组件大小。
  • 当您使用getPreferredSize布局时,
  • null无法执行任何操作,这可能会解释部分问题,因为该组件假定默认大小为0x0
  • 由于Swing不是线程安全的,因此使用Swing Timer充当你的"主循环"会更明智。而不是Thread

我不喜欢"基于组件的动画"出于这种目的,我宁愿直接去定制绘画,但那就是我

但如果必须,您应该尝试使用可用的API功能

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

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

    public Test() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("Test");
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private WallPane wallPane;

        public TestPane() {
            setLayout(null); // :(
            wallPane = new WallPane();
            wallPane.setLocation(-100, 150);
            add(wallPane);

            Timer timer = new Timer(16, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    wallPane.moveBy(2, 0);
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);            
        }

    }

    public class WallPane extends JPanel {
        public WallPane() {
            setSize(100, 100);
            setBackground(Color.RED);
        }

        public void moveBy(int xDelta, int yDelta) {
            int x = getX() + xDelta;
            int y = getY() + yDelta;
            setLocation(x, y);
        }
    }
}