作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
背景
我目前正在做一个项目,我希望能够在 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.
mousePressed
在
ClickListener
类有效:它首先检查矩形是否被点击,然后是其他矩形。这意味着如果矩形与另一个形状重叠,那么矩形总是会被优先考虑。
LinkedHashSet
对于这份工作,因为:
LinkedHashSet
来快速有效地更改顺序。 (也是恒定时间),本质上将它放在插入顺序的最后。所以这自然意味着我们必须使用集合中的最后一个元素作为最顶部的元素。这很好,因为在绘制时,我们可以按照在集合中找到的顺序遍历集合中的所有形状,因此最后一个形状将最后绘制(这意味着在所有其他形状之上)。单击时选择形状也是如此:我们遍历所有元素,然后选择我们找到的最后一个包含用户单击点的元素。如 LinkedHashSet
有一个降序迭代器(按插入顺序),那么我们还可以稍微优化在给定点击点的形状搜索,但它没有(至少对于 Java 8,其中适用以下演示/示例代码),所以我将坚持从头开始迭代,每次检查所有形状并保留找到的最后一个包含点击点的形状。 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/
我是一名优秀的程序员,十分优秀!