Java图形编程含事件处理:解决小球重复绘制问题
Hey there! Let’s fix that annoying repeated ball drawing issue in your Java graphics program. I’ve dealt with this exact problem plenty of times, so here’s the breakdown of what’s going wrong and how to fix it step by step:
Why Your Ball Keeps Getting Redrawn Repeatedly
Java Swing uses a passive drawing system—every time your component (like a JFrame) needs to refresh (e.g., window resizing, being uncovered), its paint/paintComponent method gets called automatically. If you’re drawing the ball directly inside event handlers (like key presses or mouse moves), you’re adding a new ball instance every time the event fires, leading to overlapping drawings. You’re also not clearing the old drawings before rendering the new state.
Step-by-Step Fix
1. Use a JPanel for Custom Drawing
Never draw directly on a JFrame—use a dedicated JPanel instead. Its paintComponent method is designed for custom graphics, and calling super.paintComponent(g) will automatically clear the old canvas before drawing new content.
2. Only Update State, Let paintComponent Handle Drawing
Store the ball’s coordinates as class variables. In your event handlers, only update these coordinates and call repaint() to trigger a fresh render. This ensures you’re always drawing one ball at its latest position.
3. Fix Event Listener Registration
You implemented MouseMotionListener but forgot to register it in your constructor. Also, make sure your component can receive keyboard focus so KeyListener works.
Full Corrected Code
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class MouseGui extends JFrame { // Ball coordinates and size private int ballX = 10; private int ballY = 10; private static final int BALL_SIZE = 20; // Fixed rectangle properties private final int RECT_X = 200; private final int RECT_Y = 200; private final int RECT_WIDTH = 50; private final int RECT_HEIGHT = 50; private final int MOVE_STEP = 5; // Movement speed for ball public MouseGui() { initComponents(); DrawingPanel drawingPanel = new DrawingPanel(); // Register key listener for direction control drawingPanel.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: ballY -= MOVE_STEP; break; case KeyEvent.VK_DOWN: ballY += MOVE_STEP; break; case KeyEvent.VK_LEFT: ballX -= MOVE_STEP; break; case KeyEvent.VK_RIGHT: ballX += MOVE_STEP; break; } drawingPanel.repaint(); // Trigger fresh render } }); // Register mouse motion listener for ball-to-rectangle tracking drawingPanel.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { // Calculate direction to rectangle center int rectCenterX = RECT_X + RECT_WIDTH / 2; int rectCenterY = RECT_Y + RECT_HEIGHT / 2; // Move ball toward rectangle center step by step if (Math.abs(ballX - rectCenterX) > MOVE_STEP) { ballX += (rectCenterX > ballX) ? MOVE_STEP : -MOVE_STEP; } if (Math.abs(ballY - rectCenterY) > MOVE_STEP) { ballY += (rectCenterY > ballY) ? MOVE_STEP : -MOVE_STEP; } drawingPanel.repaint(); // Trigger fresh render } }); drawingPanel.setFocusable(true); // Allow panel to receive keyboard input this.add(drawingPanel); this.setSize(400, 400); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } // Custom panel for drawing logic private class DrawingPanel extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Clear old drawings first // Draw rectangle g.setColor(Color.BLUE); g.fillRect(RECT_X, RECT_Y, RECT_WIDTH, RECT_HEIGHT); // Draw ball at current coordinates g.setColor(Color.RED); // Offset coordinates to center the ball on (ballX, ballY) g.fillOval(ballX - BALL_SIZE/2, ballY - BALL_SIZE/2, BALL_SIZE, BALL_SIZE); } } // IDE-generated init method (keep your existing code here) private void initComponents() { this.setTitle("Ball & Rectangle Controller"); } public static void main(String[] args) { SwingUtilities.invokeLater(MouseGui::new); // Run on Swing thread } }
Key Notes About This Solution
- Single Drawing Source: All rendering happens in
paintComponent, so you never have overlapping ball drawings—super.paintComponent(g)clears the canvas each time. - State-Driven Updates: Event handlers only modify the ball’s coordinates, then call
repaint()to tell Swing to redraw the latest state. - Mouse Tracking Logic: The ball smoothly moves toward the rectangle’s center whenever the mouse moves (you can adjust
MOVE_STEPto change speed). - Focus Management: Setting
setFocusable(true)on the panel ensures keyboard events are detected.
内容的提问来源于stack exchange,提问作者coc champ

