gpt4 book ai didi

Java Swing如何对自定义画作分层

转载 作者:行者123 更新时间:2023-12-04 14:44:07 26 4
gpt4 key购买 nike

背景
我目前正在做一个项目,我希望能够在 JPanel 上绘制三角形、圆形和矩形。并移动它们。
当一个图形移动时,它应该以“顶部”结束,以便它覆盖处于重叠位置的其他图形,当一个图形移动时,选择“顶部”;如果多个图形覆盖了鼠标指针的位置,则选择最上面的一个。
我的问题
如果我将它们拖到相同的位置,我无法弄清楚如何解决我的形状不会彼此重叠的问题。它们只是保持在同一层,如果我将它们全部堆叠在一起,我仍然会先拿起矩形。

最佳答案

They just stay in the same layers...


那是因为您绘制它们的顺序:
drawRec.paintComponent(g);
drawCirc.paintComponent(g);
drawTri.paintComponent(g);
这将导致 drawTri总是画在其他人的上面(因为你总是最后画)。类似地, drawRec将被涂在其他人的底部(因为你总是先画它)。然后 drawCirc将在中间绘制。这里没有这幅画的动态顺序(让我说一下)。无论您拖动与否,它们总是会按该顺序绘制。
一种解决方案可能是将它们放在某种列表或数组中,当您拖动一个形状时,将它放在列表的最后,然后将其他形状移动到它之前。如果您将其与从列表中按顺序绘制所有形状相结合,那么您将获得所需的结果。

...if i stack them all on top of each other I still pick up the rectangle first.


那是因为你的 mousePressedClickListener类有效:它首先检查矩形是否被点击,然后是其他矩形。这意味着如果矩形与另一个形状重叠,那么矩形总是会被优先考虑。
一种解决方案可能再次将所有形状放在一个数据结构中,您可以在其中修改它们的选择顺序。例如,一个列表或数组,假设离顶部越近是一个形状,那么它出现在列表中的时间就越晚。然后,当用户单击某处时,您将检查从列表中的最后一个到第一个的形状。如果你找到了一些东西,你会立即打破循环并选择你找到的东西。如果您没有找到任何东西,则用户在当前没有形状的某处单击了面板。

我几乎可以肯定,对于这个问题,必须有比列表或数组更有效的数据结构(因为你必须在线性时间内迭代所有形状才能找到被点击的形状),但我不是碰撞检测方面的专家,所以,在为了让事情变得简单,我将坚持下去。但是还有另外一个我们想做的操作:改变点击形状的绘画和选择顺序。长话短说,我们可以使用 LinkedHashSet对于这份工作,因为:
  • 它保持形状的顺序。
  • 我们可以通过首先从当前位置(恒定时间)移除点击的形状,然后将其添加回 LinkedHashSet 来快速有效地更改顺序。 (也是恒定时间),本质上将它放在插入顺序的最后。所以这自然意味着我们必须使用集合中的最后一个元素作为最顶部的元素。这很好,因为在绘制时,我们可以按照在集合中找到的顺序遍历集合中的所有形状,因此最后一个形状将最后绘制(这意味着在所有其他形状之上)。单击时选择形状也是如此:我们遍历所有元素,然后选择我们找到的最后一个包含用户单击点的元素。如 LinkedHashSet有一个降序迭代器(按插入顺序),那么我们还可以稍微优化在给定点击点的形状搜索,但它没有(至少对于 Java 8,其中适用以下演示/示例代码),所以我将坚持从头开始迭代,每次检查所有形状并保留找到的最后一个包含点击点的形状。

  • 最后,我建议您使用 API 类 java.awt.Shape用于形状,因为这使您可以创建任意形状并获得 contains检查点是否位于其中的方法、绘图/填充功能、边界、路径迭代器等...
    总结以上所有,用一个示例代码:
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Paint;
    import java.awt.Point;
    import java.awt.Shape;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.Ellipse2D;
    import java.awt.geom.Point2D;
    import java.awt.geom.Rectangle2D;
    import java.util.Collection;
    import java.util.LinkedHashSet;
    import java.util.Objects;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;

    public class NonComponentMain {

    public static Shape createRectangle(final double width,
    final double height) {
    return new Rectangle2D.Double(0, 0, width, height);
    }

    public static Shape createEllipse(final double width,
    final double height) {
    return new Ellipse2D.Double(0, 0, width, height);
    }

    public static Shape createCircle(final double radius) {
    return createEllipse(radius + radius, radius + radius);
    }

    public static class MoveableShapeHolder {
    private final Shape shape;
    private final Paint paint;
    private final Rectangle2D originalBounds;
    private double offsetX, offsetY;

    public MoveableShapeHolder(final Shape shape,
    final Paint paint) {
    this.shape = Objects.requireNonNull(shape);
    this.paint = paint;
    offsetX = offsetY = 0;
    originalBounds = shape.getBounds2D();
    }

    public void paint(final Graphics2D g2d) {
    final AffineTransform originalAffineTransform = g2d.getTransform();
    final Paint originalPaint = g2d.getPaint();
    g2d.translate(offsetX, offsetY);
    if (paint != null)
    g2d.setPaint(paint);
    g2d.fill(shape);
    g2d.setPaint(originalPaint);
    g2d.setTransform(originalAffineTransform);
    }

    public void moveTo(final double newBoundsCenterX,
    final double newBoundsCenterY) {
    offsetX = newBoundsCenterX - originalBounds.getCenterX();
    offsetY = newBoundsCenterY - originalBounds.getCenterY();
    }

    public void moveBy(final double dx,
    final double dy) {
    offsetX += dx;
    offsetY += dy;
    }

    public boolean contains(final Point2D pt) {
    return shape.contains(pt.getX() - offsetX, pt.getY() - offsetY);
    }

    public Point2D getTopLeft() {
    return new Point2D.Double(offsetX + originalBounds.getX(), offsetY + originalBounds.getY());
    }

    public Point2D getCenter() {
    return new Point2D.Double(offsetX + originalBounds.getCenterX(), offsetY + originalBounds.getCenterY()); //Like 'getTopLeft' but with adding half the size.
    }

    public Point2D getBottomRight() {
    return new Point2D.Double(offsetX + originalBounds.getMaxX(), offsetY + originalBounds.getMaxY()); //Like 'getTopLeft' but with adding the size of the bounds.
    }
    }

    public static class DrawPanel extends JPanel {

    private class MouseDrag extends MouseAdapter {
    private MoveableShapeHolder current;
    private Point origin;
    private Point2D center;

    @Override
    public void mousePressed(final MouseEvent e) {
    current = null;
    center = null;
    final Point evtLoc = e.getPoint();
    for (final MoveableShapeHolder moveable: moveables)
    if (moveable.contains(evtLoc))
    current = moveable; //Keep the last moveable found to contain the click point! It's important to be the last one, because the later the moveable appears in the collection, the closer to top its layer.

    if (current != null) { //If a shape was clicked...

    //Initialize MouseDrag's state:
    origin = e.getPoint();
    center = current.getCenter();

    //Move to topmost layer:
    moveables.remove(current); //Remove from its current position.
    moveables.add(current); //Move to last (topmost layer).

    //Rapaint panel:
    repaint();
    }
    }

    @Override
    public void mouseDragged(final MouseEvent e) {
    if (current != null) { //If we are dragging something (and not empty space), then:
    current.moveTo(center.getX() + e.getX() - origin.x, center.getY() + e.getY() - origin.y);
    repaint();
    }
    }

    @Override
    public void mouseReleased(final MouseEvent e) {
    current = null;
    origin = null;
    center = null;
    }
    }

    private final LinkedHashSet<MoveableShapeHolder> moveables;

    public DrawPanel() {
    moveables = new LinkedHashSet<>();
    final MouseAdapter ma = new MouseDrag();
    super.addMouseMotionListener(ma);
    super.addMouseListener(ma);
    }

    /**
    * Warning: all operations on the returned value must be made on the EDT.
    * @return
    */
    public Collection<MoveableShapeHolder> getMoveables() {
    return moveables;
    }

    @Override
    protected void paintComponent(final Graphics g) {
    super.paintComponent(g);
    moveables.forEach(moveable -> moveable.paint((Graphics2D) g)); //Topmost moveable is painted last.
    }

    @Override
    public Dimension getPreferredSize() {
    if (isPreferredSizeSet())
    return super.getPreferredSize();
    final Dimension preferredSize = new Dimension();
    moveables.forEach(moveable -> {
    final Point2D max = moveable.getBottomRight();
    preferredSize.width = Math.max(preferredSize.width, (int) Math.ceil(max.getX()));
    preferredSize.height = Math.max(preferredSize.height, (int) Math.ceil(max.getY()));
    });
    return preferredSize;
    }
    }

    private static void createAndShowGUI() {
    final DrawPanel drawPanel = new DrawPanel();
    final Collection<MoveableShapeHolder> moveables = drawPanel.getMoveables();

    MoveableShapeHolder moveable = new MoveableShapeHolder(createRectangle(100, 50), Color.RED);
    moveable.moveTo(100, 75);
    moveables.add(moveable);

    moveable = new MoveableShapeHolder(createCircle(40), Color.GREEN);
    moveable.moveTo(125, 100);
    moveables.add(moveable);

    moveable = new MoveableShapeHolder(createRectangle(25, 75), Color.BLUE);
    moveable.moveTo(125, 75);
    moveables.add(moveable);

    final JFrame frame = new JFrame("Click to drag");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(drawPanel);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
    }

    public static void main(final String[] args) {
    SwingUtilities.invokeLater(NonComponentMain::createAndShowGUI);
    }
    }

    以上所有内容都适用于您想要使用单个自定义组件绘制所有形状的情况。如果您愿意,您可以为每个形状创建一个自定义组件并使用 JLayeredPane用于将它们放在彼此的顶部。但是你可能需要一个自定义 LayoutManager也是(为了处理每个组件的位置)。

    关于Java Swing如何对自定义画作分层,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66960956/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com