Java Swing mousePressed和getSource()在JPanel

时间:2019-03-05 13:08:30

标签: java swing jpanel shapes mouselistener

我目前正在尝试开发一个谜题。我所得到的只是我在运动场和游戏机件上的应用程序。下一步是单击我的一个游戏作品以选择它,并能够使用箭头键移动它(此外,如果下一步(将为100像素)不包含这些内容,则我只希望它们移动)任何其他游戏零件)。

我当前遇到的问题:在主addMouseListener()上使用JPanel,然后再使用getSource()仅返回我的游戏环境(在我的代码中称为View ),但我需要它来返回所需的游戏件(例如topLeft)。我已经尝试过将getSource()强制转换为Piece,但这不起作用(Cannot cast View to Piece)。

因此,我需要找到一种添加鼠标侦听器的方法,该侦听器将返回被单击的游戏零件,以便我可以更改位置并检查是否与任何其他游戏零件发生碰撞。预先感谢!

通过@camickr编辑代码。

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.List;

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

public class Puzzle {

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

    private final static int[] SHAPE_X = { -100, 100, 100, 0, 0, -100 };
    private final static int[] SHAPE_Y = { -100, -100, 0, 0, 100, 100 };

    public Puzzle() {
        JFrame frame = new JFrame("Puzzle");
        frame.setSize(400, 600);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        View view = new View();
        frame.setContentPane(view);
        view.addMouseListener(new MouseAdapterMod(view));

        Shape polyShape = new Polygon(SHAPE_X, SHAPE_Y, SHAPE_X.length);
        Piece topLeft = new Piece(Color.YELLOW, polyShape,   0, 100, 100);
        view.pieces.add(topLeft);

        Piece topRight = new Piece(Color.CYAN, polyShape, 90, 300, 100);
        view.pieces.add(topRight);

        Piece bottomRight = new Piece(Color.GREEN,  polyShape, 180, 300, 300);
        view.pieces.add(bottomRight);

        Piece bottomLeft = new Piece(Color.RED,  polyShape, 270, 100, 300);
        view.pieces.add(bottomLeft);

        Piece square = new Piece(Color.ORANGE, new Rectangle(200, 200), 0, 100, 100);
        view.pieces.add(square);

        frame.setVisible(true);
    }

}

class View extends JPanel {

    final List<Piece> pieces = new ArrayList<>();

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

        Graphics2D gc = (Graphics2D) g;
        for (Piece piece : pieces) {
            piece.draw(gc);
        }

    }
}

class Piece {
    final Color color;
    final Shape shape;
    final int angle;

    int x;
    int y;

    Piece(Color color, Shape shape, int angle, int x, int y) {
        this.color = color;
        this.shape = shape;
        this.angle = angle;
        this.x = x;
        this.y = y;
    }

    void draw(Graphics2D gc) {
        AffineTransform tf = gc.getTransform();
        gc.translate(x, y);
        gc.rotate(Math.toRadians(angle));
        gc.setColor(color);
        gc.fill(shape);
        gc.setTransform(tf);
    }

    Shape getShape() {
    return shape;
    }
}

class MouseAdapterMod extends MouseAdapter {

    final View view;

    public MouseAdapterMod(View view) {
    this.view = view;
    }

    @Override
    public void mousePressed(MouseEvent e) {
    for(Piece piece : view.pieces) {
        if(piece.getShape().contains(e.getX(), e.getY())) {
        System.out.println("yes");
        }
    }
    }
}

2 个答案:

答案 0 :(得分:1)

  

因此,我需要找到一种添加鼠标侦听器的方法,该方法可以返回被单击的游戏件

您可以使用MouseEvent中的getX()和getY()方法。

然后,您遍历“片段” ArrayList,并在每个contains(...中包含的Shape上调用Piece方法,以查看鼠标指针是否包含在片段中。

因此,您还需要在“ Piece”类中添加getShape(...)方法,以便可以访问每个Shape的{​​{1}}。

编辑:

所以您的基本逻辑可能类似于:

Piece

然后draw(..)方法中的绘画代码就是:

//Shape polyShape = new Polygon(SHAPE_X, SHAPE_Y, SHAPE_X.length);
//Piece topLeft = new Piece(Color.YELLOW, polyShape,   0, 100, 100);
Polygon topLeftPolygon = new Polygon(SHAPE_X, SHAPE_Y, SHAPE_X.length);
topLeftPolygon.translate(100, 100);
//topLeftPolygon = ShapeUtils.rotate(...); // do rotation when required
Piece topLeft = new Piece(Color.YELLOW, topLeftPolygon);

不需要转换或翻译。

编辑2:

所以使用形状:

gc.setColor(color);
gc.fill(shape);

这当前与您的Piece类相匹配,但无论如何都需要Shape对象。请考虑所建议的概念,并且不要认为已发布的代码是完美的,因为它显然尚未经过测试。

答案 1 :(得分:1)

我看到您使用了answer中的question作为益智游戏的基础,而不是继续自己的代码。理解,我给了您一个最小,完整,可验证的示例,用于在JPanel上绘制形状。我从未说过直接使用是合适的。如果确实如此,则通过将示例设置为“最小”,可能会导致扩展代码以支持诸如命中测试之类的事情变得更加困难。但是,既然您选择继续以我的代码为基础...

您的命中测试问题来自以下事实:鼠标单击位于视图的坐标系中,但是形状被平移并旋转到其他各种位置以进行绘制。您可以通过以下方法解决此问题:反转转换,将鼠标位置转换为形状的坐标系,然后使用Shape.contains(Point2D)

class Piece {

    // ... omitted for brevity ...

    public boolean hitTest(Point point) {
        AffineTransform tf = new AffineTransform();
        tf.translate(x, y);
        tf.rotate(Math.toRadians(angle));
        try {
            Point2D pnt = tf.inverseTransform(point, null);
            return shape.contains(pnt);
        } catch (NoninvertibleTransformException e) {
            return false;
        }
    }
}

然后,只需在每片上循环,然后询问它是否在鼠标的位置下方。

class View extends JPanel {

    // ... omitted for brevity ...

    Piece hitTest(MouseEvent e) {
        Point pnt = e.getPoint();
        for (Piece piece : pieces) {
            if (piece.hitTest(pnt))
                return piece;
        }
        return null;
    }
}

请注意,我在这里返回的是Piece,而不是Shape。由于5片中有4片使用相同的polyShape形状,因此并不是很有用。

使用Java8流可以缩短hitTest()

    Piece hitTest(MouseEvent e) {
        final Point pnt = e.getPoint();
        return pieces.stream()
                     .filter(piece -> piece.hitTest(pnt))
                     .findFirst()
                     .orElse(null);
    }

如果作品可以重叠,则看起来最上面的作品是最后绘制的作品。 findFirst()将返回在给定鼠标点绘制的最底下的部分,而不是最上面的部分。以下更改将纠正该行为:

    Piece hitTest(MouseEvent e) {
        final Point pnt = e.getPoint();
        return pieces.stream()
                     .filter(piece -> piece.hitTest(pnt))
                     .reduce((first, second) -> second)
                     .orElse(null);
    }