简体   繁体   English

Java Swing 图形和事件

[英]Java Swing Graphics and Events

I am trying to make a simple game which displays circles on a frame and when clicked the circle should disappear.我正在尝试制作一个简单的游戏,它在框架上显示圆圈,单击时圆圈应该消失。 I am learning how Java Swing works and managed to draw a circle (Wow such an achievement) and figured out how events work.我正在学习 Java Swing 如何工作并设法画一个圆圈(哇,这样的成就)并弄清楚事件是如何工作的。 I added an mouseListener to the circle and when clicked for now I want a to get a console log that it has been clicked but the end result is not as expected.我在圆圈中添加了一个mouseListener,现在单击时我想要一个控制台日志,表明它已被单击,但最终结果与预期不符。 No matter where I click I always get the "click" console log.无论我在哪里点击,我总是得到“点击”控制台日志。 When I try to add a listener to a JButton for example I get the end result.例如,当我尝试将侦听器添加到 JButton 时,我得到了最终结果。 Are events different for graphics?图形的事件是否不同?


import javax.swing.*;
import javax.swing.event.MouseInputListener;
import java.awt.*;
import java.awt.event.*;
import java.sql.SQLOutput;

public class CirclePop {

    JFrame frame;
    Circle circle;

    public static void main(String[] args) {
        CirclePop circlePop = new CirclePop();
        circlePop.drawFrame();
    }

    public void drawFrame() {
        frame = new JFrame();
        circle  = new Circle();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(circle);

        circle.addMouseListener(new Click());

        frame.setSize(300, 300);
        frame.setVisible(true);
    }

    class Click implements MouseListener {

        @Override
        public void mouseClicked(MouseEvent e) {

        }

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

        @Override
        public void mouseReleased(MouseEvent e) {

        }

        @Override
        public void mouseEntered(MouseEvent e) {

        }

        @Override
        public void mouseExited(MouseEvent e) {

        }
    }
}


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

class Circle extends JPanel {

    public void paintComponent(Graphics g) {

        g.setColor(Color.red);
        g.fillOval(150, 140, 30, 30);
    }
}

First of all, you may want to extend MouseAdapter instead of implementing MouseListener .首先,您可能想要extend MouseAdapter而不是实现MouseListener This way you don't have "implement" all these empty methods.这样你就没有“实现”所有这些空方法。

Then, in your mousePressed method you just have to calculate if the click happened inside the circle.然后,在您的mousePressed方法中,您只需计算点击是否发生在圆圈内。 This is basically just Pythagoras:这基本上只是毕达哥拉斯:

static class ClickListener extends MouseAdapter {
    private final Circle circle;

    public ClickListener(Circle circle) {
        this.circle = circle;
    }


    @Override
    public void mousePressed(MouseEvent e) {
        int centerX = circle.getCenterX();
        int centerY = circle.getCenterY();
        int radius = circle.getRadius();
        int clickX = e.getX();
        int clickY = e.getY();

        // inside circle: (clickX - centerX)^2 + (clickY - centerY)^2 < radius^2
        double xSquare = Math.pow(clickX - centerX, 2);
        double ySquare = Math.pow(clickY - centerY, 2);

        if (xSquare + ySquare < radius * radius) {
            System.out.println("pressed");
        }
    }
}

I've added some fields to Circle class to get access to the properties you need for the calculation:我在Circle class 中添加了一些字段,以访问计算所需的属性:

class Circle extends JPanel {
    private final int radius = 30;
    private final int centerX = 150;
    private final int centerY = 140;

    public void paintComponent(Graphics g) {
        g.setColor(Color.red);
        g.fillOval(centerX, centerY, radius, radius);
    }
    // getter, etc.
}

You have to implement the MouseListener interface indeed, and after a mouse click, you have to check whether the mouse position is contained in the region of your circle.您确实必须实现 MouseListener 接口,并且在单击鼠标后,您必须检查鼠标 position 是否包含在您的圆圈区域中。 You could do this manually, by comparing coordinates, but this could be a bit too much work.您可以通过比较坐标手动执行此操作,但这可能有点过多的工作。
I think it's easier to rather create a Shape object(Infact this is a good time to learn about it since you're just starting out) that you fill with the respective color, and then just check whether the circle contains the mouse position.我认为创建一个 Shape 对象更容易(事实上这是了解它的好时机,因为你刚刚开始),你用相应的颜色填充,然后检查圆圈是否包含鼠标 position。
Also, check out the Shape class docs when you've got some spare time.另外,如果您有空闲时间,请查看Shape class 文档。

I've gone ahead and made changes to your code, it now uses an instance of Shape class to create a circle.我已经对您的代码进行了更改,它现在使用Shape class 的一个实例来创建一个圆圈。
Also, instead of implementing the MouseListener interface, I recommend extending MouseAdapter since you're not actually providing any meaningful implementation to any method of the interface except the mousePressed() method.此外,我建议不要实现MouseListener接口,而是扩展MouseAdapter ,因为除了mousePressed()方法之外,您实际上并没有为接口的任何方法提供任何有意义的实现。
Lastly, notice the shape.contains(event.getPoint()) in the mousePressed() method, that is what does the trick for checking the coordinates.最后,注意mousePressed()方法中的shape.contains(event.getPoint()) ,这就是检查坐标的诀窍。
The rest of the code should be familiar.代码的rest应该很熟悉了。

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

public class CirclePop {

    JFrame frame;
    Circle circle;
    public static void main(String[] args) {
        CirclePop circlePop = new CirclePop();
        circlePop.drawFrame();
    }

    public void drawFrame() {
        frame = new JFrame();
        circle  = new Circle();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(circle);

        circle.addMouseListener(new Click());

        frame.setSize(300, 300);
        frame.setVisible(true);
    }

    class Click extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            if (circle.shape.contains(e.getPoint())) {
                System.out.println("Pressed");
            }
        }
    }
}
class Circle extends JPanel {
    Shape shape;
    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        shape = new Ellipse2D.Double(150, 140, 30, 30);
        g2.setColor(Color.red);
        g2.fill(shape);
    }
}

Okay, so, this isn't going to be short好的,所以,这不会很短

Let's start with....让我们开始吧......

    frame = new JFrame();
    circle  = new Circle();

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(circle);

    circle.addMouseListener(new Click());

    frame.setSize(300, 300);
    frame.setVisible(true);

Okay, seems simple enough, but, one thing you've missed is the fact that JFrame , by default, uses a BorderLayout - this means, it will make the child component (and the centre/default position) fill all the available space of the frames viewable space好的,看起来很简单,但是,您错过的一件事是JFrame默认使用BorderLayout - 这意味着,它将使子组件(和中心/默认位置)填充所有可用空间框架可视空间

You can see this if you do something like...如果您执行类似...

    frame = new JFrame();
    circle  = new Circle();
    circle.setBackground(Color.RED);

You will now see that the Circle component occupies the entire frame, so when you click on it, you're clicking the Circle component itself.您现在将看到Circle组件占据了整个框架,因此当您单击它时,您正在单击Circle组件本身。

This isn't bad, but, you might want to change tact a little.这还不错,但是,您可能想稍微改变一下技巧。 Instead of adding the MouseListener independently of the Circle , have the Circle component make use of its own MouseListener , for example...不是独立于Circle添加MouseListener ,而是让Circle组件使用它自己的MouseListener ,例如......

class Circle extends JPanel {

    public Circle() {
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                // More to come...
            }
        });
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.setColor(Color.red);
        g.fillOval(150, 140, 30, 30);
    }
}

This means you get to control much of the logic internally to the class, makes it easier to access some of the more critical information without needing to make a bunch of, potentially, dangerous casts.这意味着您可以控制 class 内部的大部分逻辑,从而可以更轻松地访问一些更关键的信息,而无需进行大量潜在的危险转换。

So, now we just need to add the logic in to determine if the mouse was clicked within the desirable location or not...所以,现在我们只需要添加逻辑来确定鼠标是否在所需的位置被点击...

public void mouseClicked(MouseEvent e) {
    Point point = e.getPoint();
    if (point.x >= 150 && point.x <= 150 + 30 && point.y >= 140 && point.y <= 140 + 30) {
        System.out.println("You clicked me :(");
    }
}

Okay, that's... basic好吧,那是……基本的

We can simplify it a little and make use of the available functionality within the wider API by making use of the "shapes" API, for example...我们可以稍微简化一下,并通过使用“形状”API 来利用更广泛的 API 中的可用功能,例如......

class Circle extends JPanel {
    
    private Ellipse2D dot = new Ellipse2D.Double(150, 140, 30, 30);

    public Circle() {
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                Point point = e.getPoint();
                if (dot.contains(point)) {
                    System.out.println("You clicked me :(");
                }
            }
        });
    }

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

        g2d.setColor(Color.red);
        g2d.fill(dot);
        g2d.dispose();
    }
}

The benefit of this, apart from contains , is we can change the position of the shape relatively easily and our if statement contains to work这样做的好处是,除了contains之外,我们可以相对容易地更改形状的 position 并且我们的if语句 contains 可以工作

I do, highly, recommend also having a look at我非常愿意,建议您也看看

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM