You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于JFrame的JComponent鼠标拖拽绘制椭圆问题求助

问题描述

希望在添加到JFrame的JComponent上拖拽鼠标时绘制椭圆,创建了两个类:

  • 继承JComponent并实现MouseMotionListener的Painter类,重写paint(Graphics g)方法
  • 负责创建JFrame并添加组件的Main类

目前遇到的问题:

  1. 最初拖拽无反应,添加构造函数注册监听器并设置PreferredSize后,椭圆会跟随鼠标移动,但之前绘制的椭圆会消失
  2. 疑惑为何换成JPanel就能正常工作,而JComponent不行

现有代码

Painter类

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JComponent;

public class Painter extends JComponent implements MouseMotionListener {
    private int x = -10;
    private int y = -10;
    
    public Painter() {
        addMouseMotionListener(this);
        setPreferredSize(new Dimension(800,600));
    }   
    
    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.setColor(Color.black);
        g2.fillOval(x, y, 10, 10);
    }   
    
    @Override
    public void mouseDragged(MouseEvent e) {
        x = e.getX();
        y = e.getY();
        repaint();
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        // do nothing
    }
}

Main类

import javax.swing.JFrame;

public class Main{
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        Painter painter = new Painter();
        frame.setSize(800, 600);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(painter);
        frame.setVisible(true);
    }
}
问题分析与解决

1. 椭圆消失的核心原因

代码仅保存了最后一次拖拽的坐标(x,y),每次调用repaint()时,只会绘制当前坐标的椭圆,之前拖拽产生的椭圆没有被记录,因此会被覆盖消失。

2. 解决方法:保存所有绘制点

用集合存储拖拽过程中所有的坐标点,每次绘制时遍历集合,把所有椭圆都画出来。同时遵循Swing的正确绘制规范,重写paintComponent方法而非paint方法,并调用父类方法保证绘制流程正常:

修改后的Painter类代码:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;

public class Painter extends JComponent implements MouseMotionListener {
    // 用集合存储所有拖拽过的点
    private List<Point> points = new ArrayList<>();
    
    public Painter() {
        addMouseMotionListener(this);
        setPreferredSize(new Dimension(800,600));
        // 设置组件为不透明,保证背景被正确绘制
        setOpaque(true);
    }   
    
    @Override
    protected void paintComponent(Graphics g) {
        // 调用父类方法,完成背景清除等基础绘制工作
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setColor(Color.black);
        // 遍历所有点,绘制椭圆
        for (Point p : points) {
            g2.fillOval(p.x, p.y, 10, 10);
        }
    }   
    
    @Override
    public void mouseDragged(MouseEvent e) {
        // 将当前鼠标坐标加入集合
        points.add(new Point(e.getX(), e.getY()));
        repaint();
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        // do nothing
    }
}

3. JComponent与JPanel的区别

  • 透明度默认值不同:JPanel默认opaque=true(不透明),会自动填充背景;JComponent默认opaque=false(透明),不会主动绘制背景。但这不是你之前问题的核心,核心是没有保存历史绘制点。
  • 绘制流程的封装:JPanel的paint方法内部会正确调用paintComponent、paintBorder、paintChildren,而直接重写JComponent的paint方法会跳过这些规范流程,容易引发绘制问题。正确的做法是所有Swing组件都重写paintComponent方法,并调用super.paintComponent(g)。

内容的提问来源于stack exchange,提问作者pprts1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 11:27:42