Java Swing:用鼠标单击JPanel来绕圈

问题描述

我是Java的初学者,这次我想通过查找代码示例并对其进行编辑(例如从本网站进行编辑)来学习更多信息。我有一个JFrame,每次单击它(或更精确地说是JPanel)时,都会绘制一个圆圈,该圆圈将像水波纹一样向外扩展/扩展。每个圆都以一定的半径开始,并且在达到较大的半径时将被删除或重新绘制(我从10到200中选择了半径“ r”)。我为此有两个程序,它们几乎可以工作,但是缺少一些东西。如果我是正确的,我可能也知道他们的问题是什么,但我不知道如何解决它们:

一个生成的圆形,但是无论它们何时生成,它们都具有相同的大小,因为我找不到如何将半径分配给单个圆形。该代码改编自here

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Ripples extends JPanel implements ActionListener{

    public int r = 10;
    private ArrayList<Point> p;
    
    public Ripples() {
        p = new ArrayList<>();
        addMouseListener(new MouseAdapter() {
            @Override
            public void mousepressed(MouseEvent e) {
                p.add(new Point(e.getX(),e.getY()));
            }
        });
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(Color.CYAN);
        for (Point pt : p) {
            g2.drawoval(pt.x-r,pt.y-r,2*r,2*r);
        }
    }
    
        
    @Override
    public void actionPerformed(ActionEvent evt) {
        if(r<200){
            r++;
        } else {
            r = 10;
        }
        repaint();
    }
    
    public static void Gui() {
        JFrame f = new JFrame();
        Ripples p = new Ripples();
        p.setBackground(Color.WHITE);
        f.setContentPane(p);
        f.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(500,300);
        f.setVisible(true);
        Timer t = new Timer(20,p);
        t.start();
    }

    public static void main(String[] args) {
        Gui();
    }

}

一个程序(我忘记了我从哪里得到的)有不同半径的圆,具体取决于它们生成的时间,但是圆“闪烁”,因为据我所知,它们都是在同时,因为代码未像上面的代码那样包含用于分别存储和更新每个圆的数组:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Ripples extends JPanel { 
    int x,y;
    int r = 10;
    
    public Ripples(int x,int y) {
        this.x = x;
        this.y = y; 
        Timer t = new Timer(20,new ActionListener() { 
            @Override
            public void actionPerformed(ActionEvent e) { 
                if (r<200) { 
                    r++;
                } else {
                    r=10;
                }
                revalidate();
                repaint();
            }
        }); 
        t.start(); 
    }

    @Override
    public void paintComponent(Graphics g) { 
        super.paintComponent(g);
        g.setColor(Color.CYAN);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        g2.drawoval(x-r,y-r,2*r);
    }
    
    public static void Gui() {
        JFrame f = new JFrame("Water Ripples");
        JPanel p0 = new JPanel();
        p0.setBackground(Color.WHITE);
        f.add(p0);
        f.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
        f.setBounds(100,100,600,500);
        f.setVisible(true);
        f.addMouseListener(new MouseAdapter() {
            @Override
            public void mousepressed(MouseEvent e) { 
                Ripples rG = new Ripples(e.getX(),e.getY());
                rG.setBackground(Color.WHITE);
                f.add(rG); 
            }
         });
    }

    public static void main(String[] args) {
        Gui();
    }
    
}

那么,如何解决这个问题,使圈子彼此独立发展?我希望对上层代码解决方案/改进/提示,因为我认为它的结构比第二层更好。另外,我很抱歉没有将代码拆分为更多的类,并且可能不遵循命名约定。感谢您的帮助,非常感谢!

解决方法

我希望为上层代码提供解决方案/改进/提示

第二个代码更好,因为它使用:

  1. 包含有关要绘制的对象的信息的自定义类
  2. 包含要绘制的对象的ArrayList
  3. 动画的计时器。

因为我认为它的结构比第二个要好。

不是很好的理由。使用提供所需功能的代码。

自行重组代码。这是学习经验的一部分。

发出第二个代码:

  1. 它无法编译。为什么要发布无法编译的代码?这意味着您甚至还没有测试它。
  2. 创建Position对象时分配初始半径。
  3. 当计时器触发时,您需要遍历ArrayList以更新每个Position对象的半径。
  4. 在绘画代码中使用Position对象的半径。

作为一项附加更改,可以调用PositionRipple。然后,您可以为波动的Color添加另一个自定义属性。然后,当您将Ripple添加到ArrayList时,会随机生成一个Color。然后,在绘画方法中,使用Ripple类的Color属性。这是使对象和绘画更灵活,更动态的方式。

,

我在您的Circle代码中添加了Ripples类。这样,ActionListener就可以独立对待每个圆。

我通过调用SwingUtilities invokeLater方法来启动GUI。此方法可确保在Event Dispatch Thread上创建并执行Swing组件。

这是代码。

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Ripples extends JPanel implements ActionListener {

    private List<Circle> circles;

    public Ripples() {
        circles = new ArrayList<>();
        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent event) {
                circles.add(new Circle(event.getPoint()));
            }
        });
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(Color.CYAN);
        g2.setStroke(new BasicStroke(3f));
        
        for (Circle circle : circles) {
            Point p = circle.getCenter();
            int radius = circle.getRadius();
            g2.drawOval(p.x - radius,p.y - radius,2 * radius,2 * radius);
        }
    }

    @Override
    public void actionPerformed(ActionEvent evt) {
        for (Circle circle : circles) {
            circle.incrementRadius();
        }
        repaint();
    }

    public static void createGUI() {
        JFrame f = new JFrame("Ripples");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Ripples p = new Ripples();
        p.setBackground(Color.WHITE);
        p.setPreferredSize(new Dimension(500,500));
        f.setContentPane(p);

        f.pack();
        f.setLocationByPlatform(true);
        f.setVisible(true);

        Timer t = new Timer(20,p);
        t.start();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createGUI();
            }
        });
    }

    public class Circle {

        private int radius;

        private final Point center;

        public Circle(Point center) {
            this.center = center;
            this.radius = 10;
        }

        public void incrementRadius() {
            radius += 1;
            radius = (radius > 200) ? 10 : radius;
        }

        public int getRadius() {
            return radius;
        }

        public Point getCenter() {
            return center;
        }

    }

}

编辑后添加:

我重新编写了Ripples类代码以分离问题。我创建了一个DrawingPanel类来保存绘图面板,一个RipplesListener类来保存MouseAdapter代码,一个Animation类来保存运行{圆圈动画,一个Runnable类,用于容纳RipplesModel个实例的List,最后是Circle类。

本来可以将Swing Circle用于动画,但是我对创建和运行自己的动画线程更为熟悉。

是的,此代码比原始示例更复杂。此处使用的编码样式可用于更大,更复杂的Swing GUI开发。

这是修订的代码。我希望这是一个更好的例子。

Timer