簡體   English   中英

Java Swing:通過在 JPanel 上單擊鼠標制作一個不斷增長的圓圈

[英]Java Swing: Making a growing circle by mouse click on JPanel

我是 Java 的初學者,這次我試圖通過查找代碼示例並編輯它們來了解更多信息,例如從這個網站。 我有一個 JFrame,每次點擊它(或更准確地說是其中的 JPanel)時,都會繪制一個圓圈,該圓圈將像水波紋一樣向外擴展/擴展。 每個圓都以一定的半徑開始,當達到更大的半徑時將被刪除或重新繪制(我選擇了半徑“r”從 10 到 200)。 我有兩個程序,它們幾乎可以工作,但缺少一些東西。 如果我是對的,我可能也知道他們的問題是什么,但我不知道如何解決它們:

一個生成越來越大的圓,但無論何時生成,它們都具有相同的大小,因為我不知道如何將半徑分配給單個圓。 代碼改編自這里

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,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. 當 Timer 觸發時,您需要遍歷 ArrayList 以更新每個Position對象的半徑。
  4. Position對象的半徑用於繪制代碼。

作為附加的更改,可以調用PositionRipple 然后您可以為漣漪的Color添加另一個自定義屬性。 然后,當您將Ripple添加到 ArrayList 時,您會隨機生成一個顏色。 然后在繪制方法中使用Ripple類的 Color 屬性。 這就是您使對象和繪畫更加靈活和動態的方式。

我在你的Ripples代碼中添加了一個Circle類。 這允許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類來保存Circle實例List ,最后是Circle類。

我本可以為動畫使用 Swing Timer ,但我更熟悉創建和運行我自己的動畫線程。

是的,此代碼比原始示例更復雜。 此處使用的編碼風格可以用於更大、更復雜的 Swing GUI 開發。

這是修改后的代碼。 我希望這是一個更好的例子。

import java.awt.BasicStroke;
import java.awt.BorderLayout;
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.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

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

public class Ripples implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Ripples());
    }

    private Animation animation;

    private DrawingPanel drawingPanel;

    private RipplesModel model;

    public Ripples() {
        model = new RipplesModel();
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Ripples");
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent event) {
                stopAnimation();
                frame.dispose();
                System.exit(0);
            }
        });

        drawingPanel = new DrawingPanel(model);
        frame.add(drawingPanel, BorderLayout.CENTER);

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

        animation = new Animation(this, model);
        new Thread(animation).start();
    }

    public void repaint() {
        drawingPanel.repaint();
    }

    private void stopAnimation() {
        if (animation != null) {
            animation.setRunning(false);
        }
    }

    public class DrawingPanel extends JPanel {

        private static final long serialVersionUID = 1L;

        private RipplesModel model;

        public DrawingPanel(RipplesModel model) {
            this.model = model;
            setBackground(Color.WHITE);
            setPreferredSize(new Dimension(500, 500));
            addMouseListener(new RipplesListener(model));
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setStroke(new BasicStroke(3f));

            List<Circle> circles = model.getCircles();
            for (Circle circle : circles) {
                Point p = circle.getCenter();
                int radius = circle.getRadius();
                g2.setColor(circle.getColor());
                g2.drawOval(p.x - radius, p.y - radius,
                        2 * radius, 2 * radius);
            }
        }

    }

    public class RipplesListener extends MouseAdapter {

        private RipplesModel model;

        public RipplesListener(RipplesModel model) {
            this.model = model;
        }

        @Override
        public void mousePressed(MouseEvent event) {
            model.addCircle(new Circle(event.getPoint(),
                    createColor()));
        }

        private Color createColor() {
            Random random = new Random();
            int r = random.nextInt(255);
            int g = random.nextInt(255);
            int b = random.nextInt(255);
            return new Color(r, g, b);
        }
    }

    public class Animation implements Runnable {

        private volatile boolean running;

        private Ripples frame;

        private RipplesModel model;

        public Animation(Ripples frame, RipplesModel model) {
            this.frame = frame;
            this.model = model;
            this.running = true;
        }

        @Override
        public void run() {
            while (running) {
                sleep(20L);
                incrementRadius();
            }
        }

        private void incrementRadius() {
            List<Circle> circles = model.getCircles();
            for (Circle circle : circles) {
                circle.incrementRadius();
            }
            repaint();
        }

        private void sleep(long delay) {
            try {
                Thread.sleep(delay);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        private void repaint() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    frame.repaint();
                }
            });
        }

        public synchronized void setRunning(boolean running) {
            this.running = running;
        }

    }

    public class RipplesModel {

        private List<Circle> circles;

        public RipplesModel() {
            this.circles = new ArrayList<>();
        }

        public void addCircle(Circle circle) {
            this.circles.add(circle);
        }

        public List<Circle> getCircles() {
            return circles;
        }

    }

    public class Circle {

        private int radius;

        private final Color color;

        private final Point center;

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

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

        public Color getColor() {
            return color;
        }

        public int getRadius() {
            return radius;
        }

        public Point getCenter() {
            return center;
        }

    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM