我正在尝试制作一个简单的动画,其中一个矩形从屏幕开始(在屏幕右边缘的右侧)并向左移动。因此,在这种情况下,我的框架宽度为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);
}
}
答案 0 :(得分:3)
我可以在您的程序中找到一些错误
您使用的是(可能不是在这种情况下,根据@ MadProgrammer在下面的评论),这只是另一种方法null layout
,请参阅Null layout is evil以及this question中的答案,了解您应该避免使用的原因。
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
}
});
}
您没有在super.paintComponent()
类的paintComponent()
方法上调用Wall
,这可能会破坏画颜链,始终先调用它。
您正在扩展JComponent
,最好扩展JPanel
并使用Shape API对其进行自定义绘制
考虑到以上所有因素,您可以拥有类似这样的代码:
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));
}
}
这将产生类似这样的输出:
答案 1 :(得分:2)
setLocation(wallX, wallY);
,绘画应该绘制当前状态永远不会改变它。 getPreferredSize
布局时,null
无法执行任何操作,这可能会解释部分问题,因为该组件假定默认大小为0x0
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);
}
}
}