Java - событие действия MouseListener в paintComponent

Здесь у меня есть код, который рисует прямоугольник в позиции mouseClicked с помощью paintComponent. Я могу получить выходное сообщение, но все, что связано с графикой и .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() в этой программе? Благодарю.

2
добавлено отредактировано
Просмотры: 1
de
Вы добавляете новый MouseListener в p каждый раскрашенный фрейм!
добавлено автор Xeon, источник
@Xeon есть ли какой-либо другой способ этого? Потому что, когда я когда-либо помещал его за пределы paintComponent (), он дает мне ошибку «неизвестной переменной», а gD не входит в область MouseListener.
добавлено автор user1934283, источник

2 ответы

Некоторые предложения по текущему коду:

  • Посмотрите схему именования классов i.e testclass должен быть TestClass или даже лучше Test (но это выбор nit). Все имена классов начинаются с заглавной буквы, и каждое новое слово после этого капитализируется.

  • Не используйте JFrame без необходимости.

  • Не звоните setBounds в JFrame скорее используйте соответствующий LayoutManager и/или переопределите getPreferredSize() из JPanel и вернуть размеры, которые соответствуют его содержимому.

  • Всегда вызывайте pack() в JFrame , прежде чем устанавливать его видимым (принимая во внимание выше).

  • Используйте MouseAdapter vs MouseListener

  • Не звоните moveBall() в paintComponent , скорее позвоните ему в Timer , который перерисовывает экран, а не только немного лучше. но мы также не должны выполнять, возможно, длительные задачи в методах рисования.

Что касается вашей проблемы, я думаю, что ваша логика немного искажена.

Один из подходов позволит увидеть, что Rectangle (или Rectangle2D ) заменяется собственным пользовательским классом (который позволит нам хранить атрибуты, такие как цвет и т. Д.). Ваш ball также будет иметь свой собственный класс, который имеет метод moveBall() и его атрибуты, такие как x и y и т. д. На каждом repaint() ваш JPanel будет вызывать метод для перемещения шара, сам JPanel может обернуть moveBall() в своем собственном общедоступном методе, который мы можем вызвать из таймера, который перерисовывает экран.

Ниже приведен пример вашего кода с приведенными выше исправлениями (пожалуйста, проанализируйте его, и если у вас есть какие-либо вопросы, дайте мне знать):

enter image description here

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 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;
        }
    }
}
2
добавлено
@ user1934283 С удовольствием, и рад, что вы заработали и поняли код. И не проблема, мне нравится давать код, который следует за лучшими практиками, а не просто исправлять проблему с помощью другого хакерского или нехорошего пути. И теперь у вас есть некоторые основы того, как классы могут использоваться в вашем коде, чтобы упростить вещи и сделать их более многоразовыми.
добавлено автор David Kroukamp, источник
Спасибо за быстрый ответ и правильный код. После вашего первоначального ответа я был замешан в части рисования прямоугольника, используя ArrayList, но позже, прочитав код полностью, я понял. Я все еще изучаю классы/методы, поэтому мой первоначальный код, возможно, был беспорядок.
добавлено автор user1934283, источник

First of all the paint component is called every time swing needs to redraw the component.
And you are adding a new instance of mouse listener to the panel every time the paint is called.

Just move the line
p.addMouseListener(new MouseListener() {...}
out of the paint component, preferably after the initialization of the panel.

шаблон по умолчанию

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

});

Надеюсь это поможет.

0
добавлено
никогда не забывайте вызывать реализацию переопределенных методов super.XXX i.e super.paintComponent (g) , если вы не игнорируете ее целенаправленно или код будет работать неправильно.
добавлено автор David Kroukamp, источник
umm я попробовал, что предложил, но я получаю ошибку «не могу найти символ символа: переменная gD».
добавлено автор user1934283, источник