【问题标题】:Java Swing mousePressed and getSource() not showing drawed shapes on JPanelJava Swing mousePressed 和 getSource() 未在 JPanel 上显示绘制的形状
【发布时间】:2019-03-05 13:08:30
【问题描述】:

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

我目前遇到的问题:在我的主要 JPanel 上使用 addMouseListener() 然后使用 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");
        }
    }
    }
}

【问题讨论】:

  • 您可能希望使用MouseEvent 中的getX()getY() 来查找选择了哪个Piece
  • @Arnaud 使用getComponentAt()View 的任何类似方法?我也试过了,一般也只返回View

标签: java swing jpanel shapes mouselistener


【解决方案1】:

所以,我需要找到一种方法来添加一个鼠标侦听器,该侦听器返回被点击的游戏片段

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

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

因此,您还需要在“Piece”类中添加一个getShape(...) 方法,以便您可以访问每个PieceShape

编辑:

所以你的基本逻辑可能是这样的:

//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);

那么draw(..)方法中的绘画代码就是:

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

无需转换或翻译。

编辑 2:

所以使用形状:

//topLeftPolygon = ShapeUtils.rotate(...); // do rotation when required
//Piece topLeft = new Piece(Color.YELLOW, topLeftPolygon);
Shape topLeftShape = ShapeUtils.rotate(...); // do rotation when required
Piece topLeft = new Piece(Color.YELLOW, topLeftShape);

这与您的 Piece 类相匹配,该类无论如何都需要一个 Shape 对象。请考虑建议的概念,不要认为发布的代码是完美的,因为它显然没有经过测试。

【讨论】:

  • 谢谢你,这部分奏效了。我现在遇到的唯一问题:并非所有部分都包含 x 和 y 坐标,即使我直接单击形状的中间(请参阅我编辑的帖子以获取我的代码并尝试单击该部分在左下角,你会看到,它不起作用)。
  • @Nordic88,问题是您在绘制形状时会通过平移和旋转来改变形状,因此您将苹果与橙子进行比较。您需要做的是在将形状添加到您的片段之前平移和旋转形状。然后你只需简单地绘制形状。 Polygon 类支持 translate(...) 方法。然后您可以查看Playing With Shapes 中的ShapeUtils。它支持rotate(...) 方法。
  • 谢谢,但我不太明白你的意思。所以我应该在我的Piece 中继承Polygon 并在那里进行绘图,然后在View 中绘制Piece?
  • 问题是你有一个多边形。所以当你调用它的contains(...) 方法时,你总是在比较同一个多边形。然后,当您绘制多边形时,您平移 x/y 位置并旋转它,使多边形出现在面板上的不同位置。相反,您需要创建单独的多边形。每个多边形在您创建时都会被平移/旋转。然后你就按原样绘制多边形,不需要绘制代码中的平移/旋转逻辑。
  • 谢谢!这工作得很好,但旋转仍然是一个问题,因为ShapeUtils#rotate 返回一个形状,所以我不能使用类似topLeftPolygon = ShapeUtils.rotate(...) 的东西并将其转换为Polygon 也不起作用。我宁愿使用旋转而不是更改每个 x/y 坐标来存档旋转的多边形。
【解决方案2】:

我看到你使用了我的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 形状,所以这不是很有用。

hitTest() 可以使用 Java8 流缩短:

    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);
    }

【讨论】:

    猜你喜欢
    • 2016-02-16
    • 1970-01-01
    • 2012-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-19
    • 2021-06-13
    • 1970-01-01
    相关资源
    最近更新 更多