【问题标题】:Java - MouseListener Action Event in paintComponentJava - PaintComponent 中的 MouseListener 动作事件
【发布时间】:2012-12-28 10:58:18
【问题描述】:

这里我有一个代码,它使用paintComponent在mouseClicked位置上绘制一个矩形。我可以得到输出消息,但任何与图形和.draw()相关的东西都不起作用。

代码:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public final class testclass extends JFrame {

    static JPanel p;
    Timer t;
    int x = 1;
    int y = 1;
    int xspeed = 1;
    int yspeed = 1;

    public testclass() {
        initComponents();
        this.setBounds(100, 300, 500, 500);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        t.start();
        this.add(p);
    }

    public void initComponents() {
        final ActionListener action = new ActionListener() {

            public void actionPerformed(ActionEvent evt) {
                System.out.println("Hello!");
                p.repaint();
            }
        };

        t = new Timer(50, action);
        p = new JPanel() {

            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                final Graphics2D gD = (Graphics2D) g;
                moveBALL();
                gD.drawOval(x, y, 25, 25);

                p.addMouseListener(new MouseListener() {

                    @Override
                    public void mouseReleased(MouseEvent e) {
                        System.out.println("a");
                    }

                    @Override
                    public void mousePressed(MouseEvent e) {
                        System.out.println("b");
                    }

                    @Override
                    public void mouseExited(MouseEvent e) {
                        System.out.println("c");
                    }

                    @Override
                    public void mouseEntered(MouseEvent e) {
                        System.out.println("d");
                    }

                    @Override
                    public void mouseClicked(MouseEvent e) {
                        gD.drawRect(e.getX(), e.getY(), 10, 60);
                        gD.setColor(Color.green);
                        System.out.println("clicked");
                    }
                });
            }

            void moveBALL() {
                x = x + xspeed;
                y = y + yspeed;
                if (x < 0) {
                    x = 0;
                    xspeed = -xspeed;
                } else if (x > p.getWidth() - 20) {
                    x = p.getWidth() - 20;
                    xspeed = -xspeed;
                }
                if (y < 0) {
                    y = 0;
                    yspeed = -yspeed;
                } else if (y > p.getHeight() - 20) {
                    y = p.getHeight() - 20;
                    yspeed = -yspeed;
                }
            }
        };
    }

    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                new testclass().setVisible(true);
                p.setBackground(Color.WHITE);
            }
        });
    }
}

在这个程序中实现 mouseListener() 的正确方法是什么? 谢谢。

【问题讨论】:

  • 您正在为p 绘制的每一帧添加新的 MouseListener!
  • @Xeon 有没有其他方法可以解决这个问题?因为我将它放在paintComponent() 之外的任何地方都会出现“未知变量”错误,即 gD 不在 MouseListener 的范围内。跨度>

标签: java swing events draw paintcomponent


【解决方案1】:

对当前代码的一些建议:

  • Watch 类命名方案,即 testclass 应该是 TestClass 或更好的 Test(但那是挑剔)。所有类名都以大写字母开头,之后的每个新词都大写。

  • 不要不必要地扩展JFrame

  • 不要在JFrame 上调用setBounds,而是使用适当的LayoutManager 和/或覆盖JPanelgetPreferredSize() 并返回适合其内容的尺寸。

  • 在设置为可见之前,始终在 JFrame 上调用 pack()(考虑到上述情况)。

  • 使用MouseAdapterMouseListener

  • 不要在paintComponent 中调用moveBall(),而是在重新绘制屏幕的Timer 中调用它,不仅设计稍好,而且我们也不应该在绘制方法中执行可能长时间运行的任务。

至于你的问题,我觉得你的逻辑有点歪。

一种方法是将Rectangle(或Rectangle2D)替换为它自己的自定义类(这将允许我们存储颜色等属性)。您的 ball 也将有自己的类,该类具有方法 moveBall() 及其属性,如 xy 位置等。在每个 repaint() 上,您的 JPanel 将调用该方法移动球,JPanel 本身可以将moveBall() 包装在它自己的公共方法中,然后我们可以从重绘屏幕的计时器中调用它。

以下是您的代码示例,已实现上述修复(请分析它,如果您有任何问题,请告诉我):

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.*;

public class Test {

    private MyPanel p;
    private Timer t;

    public Test() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        initComponents();
        frame.add(p);

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

        t.start();
    }

    private void initComponents() {
        final ActionListener action = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                p.moveEntities();//moves ball etc
                p.repaint();
            }
        };

        t = new Timer(50, action);
        p = new MyPanel();

        p.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                p.addEntity(e.getX(), e.getY(), 10, 50, Color.GREEN);
                System.out.println("clicked");
            }
        });

        p.setBackground(Color.WHITE);
    }

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

class MyPanel extends JPanel {

    int width = 300, height = 300;
    ArrayList<MyRectangle> entities = new ArrayList<>();
    MyBall ball = new MyBall(10, 10, 25, 25, Color.RED, width, height);

    void addEntity(int x, int y, int w, int h, Color c) {
        entities.add(new MyRectangle(x, y, w, h, c));
    }

    void moveEntities() {
        ball.moveBALL();
    }

    @Override
    protected void paintComponent(Graphics grphcs) {
        super.paintComponent(grphcs);
        Graphics2D g2d = (Graphics2D) grphcs;

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2d.setColor(ball.getColor());
        g2d.fillOval((int) ball.x, (int) ball.y, (int) ball.width, (int) ball.height);

        for (MyRectangle entity : entities) {
            g2d.setColor(entity.getColor());
            g2d.fillRect((int) entity.x, (int) entity.y, (int) entity.width, (int) entity.height);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(width, height);
    }
}

class MyRectangle extends Rectangle2D.Double {

    Color color;

    public MyRectangle(double x, double y, double w, double h, Color c) {
        super(x, y, w, h);
        color = c;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public Color getColor() {
        return color;
    }
}

class MyBall extends Ellipse2D.Double {

    int xspeed = 1;
    int yspeed = 1;
    Color color;
    private final int maxWidth;
    private final int maxHeight;

    public MyBall(double x, double y, double w, double h, Color c, int maxWidth, int maxHeight) {
        super(x, y, w, h);
        color = c;
        this.width = w;//set width and height of Rectangle2D
        this.height = h;
        //set max width and height ball can move
        this.maxWidth = maxWidth;
        this.maxHeight = maxHeight;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public Color getColor() {
        return color;
    }

    void moveBALL() {
        x = x + xspeed;
        y = y + yspeed;
        if (x < 0) {
            x = 0;
            xspeed = -xspeed;
        } else if (x > maxWidth - ((int) getWidth() / 2)) {// i dont like hard coding values its not good oractice and resuaibilty is diminshed
            x = maxWidth - ((int) getWidth() / 2);
            xspeed = -xspeed;
        }
        if (y < 0) {
            y = 0;
            yspeed = -yspeed;
        } else if (y > maxHeight - ((int) getHeight() / 2)) {
            y = maxHeight - ((int) getHeight() / 2);
            yspeed = -yspeed;
        }
    }
}

【讨论】:

  • 感谢您的快速回复和正确的代码。在您最初的回复之后,我对使用 ArrayList 的矩形绘制部分感到困惑,但后来在仔细阅读代码后我明白了这一点。我还在学习类/方法,所以我的初始代码可能是一团糟。
  • @user1934283 很高兴,很高兴你让它工作并理解了代码。这不是问题,我也喜欢提供遵循最佳实践的代码,而不是仅仅用另一种 hacky 或不好的方法来解决问题。现在您已经了解了如何在代码中使用类来简化事情并使其更具可重用性。
【解决方案2】:

首先每次swing需要重绘组件时都会调用paint组件。
每次调用绘画时,您都会向面板添加一个新的鼠标侦听器实例。

只需移动线
p.addMouseListener(new MouseListener() {...}
退出paint组件,最好在面板初始化之后。

默认模板是

JPanel p = new JPanel(){
    @Override
    public void paintComponent(Graphics g) {
    }
};
p.addMouseListener(new MouseListener()  or new MouseAdapter()
//Your overridden methods

});

希望这会有所帮助。

【讨论】:

  • 嗯,我尝试了你的建议,但我收到“找不到符号符号:变量 gD”错误。
  • 永远不要忘记调用 super.XXXsuper.paintComponent(g) 实现被覆盖的方法,除非您有意忽略它或代码无法正常运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多