Java JFrame双弹跳球图形显示异常问题求助
Hey there! Let's break down what's going wrong with your bouncing balls and fix them step by step.
First, let's identify the core issues:
- Layout Manager Misuse: Using
FlowLayoutforces each of yourMaininstances (I assume it's a JPanel subclass) to act as separate, small components. This means each ball's coordinates are relative to its tiny panel, not the whole window—plus, multiple components redrawing independently causes that stuttering. - EDT Violations: Your
while(true)loop withThread.sleep()runs directly in the main thread, which breaks Swing's single-thread rule. All UI updates should happen on the Event Dispatch Thread (EDT) to avoid freezes and glitches. - Component Structure: Having one component per ball is inefficient; you should draw all balls on a single panel for better control and performance.
Here's the Fixed Solution
Let's restructure the code to follow Swing best practices:
1. Create a Ball Class to Encapsulate Ball Data
This keeps each ball's properties and movement logic clean and reusable:
import java.awt.Color; public class Ball { private int x; private int y; private int xDir; private int yDir; private final Color color; private static final int SIZE = 50; public Ball(int startX, int startY, Color color) { this.x = startX; this.y = startY; this.color = color; // Random initial direction for natural movement this.xDir = Math.random() > 0.5 ? 1 : -1; this.yDir = Math.random() > 0.5 ? 1 : -1; } public void move(int panelWidth, int panelHeight) { x += xDir; y += yDir; // Bounce off left/right edges if (x < 0 || x > panelWidth - SIZE) { xDir *= -1; } // Bounce off top/bottom edges if (y < 0 || y > panelHeight - SIZE) { yDir *= -1; } } // Getters for drawing public int getX() { return x; } public int getY() { return y; } public Color getColor() { return color; } public int getSize() { return SIZE; } }
2. Build a Single BallPanel to Manage All Balls
This panel handles drawing all balls and updating their positions using a Swing Timer (the correct way to do Swing animations):
import java.awt.Graphics; import java.awt.Color; import javax.swing.JPanel; import javax.swing.Timer; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; public class BallPanel extends JPanel { private List<Ball> balls; public BallPanel() { balls = new ArrayList<>(); // Add your two balls balls.add(new Ball(50, 50, Color.RED)); balls.add(new Ball(200, 200, Color.BLUE)); // Swing Timer triggers updates on the EDT (no more thread issues!) Timer animationTimer = new Timer(5, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateAllBalls(); repaint(); } }); animationTimer.start(); } private void updateAllBalls() { for (Ball ball : balls) { ball.move(getWidth(), getHeight()); } } // Always use paintComponent for custom Swing drawing (not paint!) @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Clear the panel background g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); // Draw every ball for (Ball ball : balls) { g.setColor(ball.getColor()); g.fillOval(ball.getX(), ball.getY(), ball.getSize(), ball.getSize()); } } }
3. The Main Class to Launch the App
We initialize the UI on the EDT to follow Swing rules:
import javax.swing.JFrame; public class BouncingBallsDemo { public static void main(String[] args) { // Run UI setup on the Event Dispatch Thread javax.swing.SwingUtilities.invokeLater(() -> { JFrame frame = new JFrame("Bouncing Balls"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 400); frame.add(new BallPanel()); frame.setVisible(true); }); } }
Key Improvements Explained
- Single Panel Drawing: All balls live in one panel, so their coordinates are relative to the whole window, and we only need one repaint call per frame.
- Swing Timer: Ensures all animation updates run on the EDT, eliminating stutters and thread safety issues.
- Encapsulated Ball Logic: The
Ballclass keeps code organized—adding more balls later is as simple as callingballs.add(new Ball(...)). paintComponentInstead ofpaint: This is the standard method for custom drawing in Swing, as it avoids interfering with border/child component drawing.
内容的提问来源于stack exchange,提问作者John Costa
相关产品推荐
相关产品推荐

