我正在尝试将图像图标拖放到屏幕上,但每当我在移动图标后单击菜单栏时,它都会重置到原来的位置。我认为这与 Menu 类中的屏幕对象没有更新以反映 Window 类的更改有关。有人可以帮忙吗?
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Tester {
public Tester()
{
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Window win = new Window();
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
public class Window extends JFrame
{
private Menu menu;
public Window()
{
setDefaultLookAndFeelDecorated(true);
menu = new Menu(this);
JMenuBar menuBar = menu.getMenuBar();
setJMenuBar(menuBar);
add(menu.screen);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
}
public class MouseHandler extends MouseAdapter{
private Point offset;
public MouseHandler()
{
}
@Override
public void mousePressed(MouseEvent e) {
offset = e.getPoint();
}
@Override
public void mouseDragged(MouseEvent e) {
int x = e.getPoint().x - offset.x;
int y = e.getPoint().y - offset.y;
JLabel component = (JLabel) e.getComponent();
Point location = component.getLocation();
location.x += x;
location.y += y;
component.setLocation(location);
}
}
public class Screen extends JPanel
{
private MouseHandler m;
public Screen()
{
setPreferredSize(new Dimension(700, 500));
m = new MouseHandler();
}
public void addObject()
{
BufferedImage move = new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB);
Graphics2D x = move.createGraphics();
x.setStroke(new BasicStroke(1));
x.drawRect(0, 0, 20, 20);
JFrame f = new JFrame();
f.pack();
f.paint(x);
JLabel lab = new JLabel(new ImageIcon(move));
lab.addMouseListener(m);
lab.addMouseMotionListener(m);
add(lab);
}
}
public class Menu extends JPanel implements ActionListener
{
private Window window;
private JMenuBar menuBar;
private JMenu menu;
private JMenuItem menuItem;
public Screen screen = new Screen();
public Menu(Window window)
{
this.window = window;
menuBar = new JMenuBar();
menu = new JMenu("1");
menuItem = new JMenuItem("2");
menuBar.add(menu);
menu.add(menuItem);
menuItem.addActionListener(this);
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
screen.addObject();
screen.revalidate();
screen.repaint();
}
public JMenuBar getMenuBar()
{
return this.menuBar;
}
}
}
抱歉缺乏详细信息,我正试图在上班之前发布此信息。有些变量可能是多余的,因为我正在尝试不同的事情并且没有费心删除它们。
我尝试更改它,以便它可以重新验证并重新绘制,但这只是解决了我遇到的另一个问题。
编辑:我为 MRE 制作了一个更简单的版本。我相信这可能与布局有关,就好像我调用 revalidate 方法一样,它对对象做了同样的事情。
Screen 对象的 LayoutManager(恰好是 FlowLayout,因为这是默认布局)决定子组件的放置位置以及它们的大小。 此过程在验证面板时发生,也可能因其他原因而发生。 每次发生时,您的
component.setLocation(location);
调用的效果都会被丢弃。
应用程序不应显式设置组件的位置(或大小)。 获取组件的尺寸,即使是像标签这样简单的东西,也可能是一个复杂的过程。 文本是根据字体的磅值和显示器的点距呈现的。 图像可以使用系统的 HiDPI 设置进行缩放。
所有这些都是由 Swing 处理的。 组件能够报告自己的首选尺寸,布局管理器将适应这些尺寸,并根据布局管理器的合同放置东西。 (例如,BorderLayout 可以在中心放置一个组件,在两侧放置一个组件。)
但是你不希望东西自动放置; 您希望它们显示在用户拖动它们的任何位置。 这意味着,您的需求与所有 LayoutManager 直接冲突。
因此,您不应该使用子组件来显示图标。 相反,直接在 JPanel 上绘制它们。
这需要一些东西:
这是一个例子:
import java.io.Serial;
import java.io.Serializable;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Map;
import java.util.EnumMap;
import java.util.Optional;
import java.util.Objects;
import java.awt.EventQueue;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.AWTEvent;
import java.awt.image.BufferedImage;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JFrame;
public class TableArranger
extends JPanel {
@Serial
private static final long serialVersionUID = 1;
public enum TableType {
RED,
BLUE
}
public static class Table
implements Serializable {
@Serial
private static final long serialVersionUID = 1;
private final Point location = new Point();
private final TableType type;
public Table(TableType type) {
Objects.requireNonNull(type, "Type cannot be null.");
this.type = type;
}
public TableType getType() {
return type;
}
public Point getLocation() {
return new Point(location);
}
public void moveTo(Point newLocation) {
location.setLocation(newLocation);
}
}
private final Map<TableType, BufferedImage> icons;
private final Collection<Table> tables = new ArrayList<>();
private Table draggedTable;
private Point relativeDragLocation;
public TableArranger() {
enableEvents(
AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
BufferedImage redIcon =
new BufferedImage(20, 20, BufferedImage.TYPE_INT_ARGB);
Graphics g = redIcon.createGraphics();
g.setColor(Color.RED);
g.drawRect(0, 0, 19, 19);
g.dispose();
BufferedImage blueIcon =
new BufferedImage(20, 20, BufferedImage.TYPE_INT_ARGB);
g = blueIcon.createGraphics();
g.setColor(Color.BLUE);
g.drawRect(0, 0, 19, 19);
g.dispose();
this.icons = new EnumMap<>(
Map.of(TableType.RED, redIcon, TableType.BLUE, blueIcon));
}
@Override
public Dimension getPreferredSize() {
return new Dimension(700, 500);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Table table : tables) {
Point location = table.getLocation();
TableType type = table.getType();
BufferedImage icon = icons.get(type);
g.drawImage(icon, location.x, location.y, this);
}
}
private Optional<Table> tableAt(Point point) {
for (Table table : tables) {
Point location = table.getLocation();
TableType type = table.getType();
BufferedImage icon = icons.get(type);
int width = icon.getWidth();
int height = icon.getHeight();
if (point.x >= location.x && point.x < location.x + width &&
point.y >= location.y && point.y < location.y + height) {
return Optional.of(table);
}
}
return Optional.empty();
}
@Override
protected void processMouseEvent(MouseEvent event) {
if (event.getButton() == MouseEvent.BUTTON1) {
int id = event.getID();
switch (id) {
case MouseEvent.MOUSE_PRESSED:
Point clickLocation = event.getPoint();
Optional<Table> clickedTable = tableAt(clickLocation);
if (clickedTable.isPresent()) {
draggedTable = clickedTable.get();
Point tableLocation = draggedTable.getLocation();
relativeDragLocation = new Point(
clickLocation.x - tableLocation.x,
clickLocation.y - tableLocation.y);
event.consume();
}
break;
case MouseEvent.MOUSE_RELEASED:
draggedTable = null;
relativeDragLocation = null;
break;
default:
break;
}
}
super.processMouseEvent(event);
}
@Override
protected void processMouseMotionEvent(MouseEvent event) {
int modifiers = event.getModifiersEx();
if (event.getID() == MouseEvent.MOUSE_DRAGGED &&
(modifiers | MouseEvent.BUTTON1_DOWN_MASK) != 0 &&
draggedTable != null) {
Point oldLocation = draggedTable.getLocation();
TableType type = draggedTable.getType();
BufferedImage icon = icons.get(type);
int width = icon.getWidth();
int height = icon.getHeight();
Point newLocation = event.getPoint();
newLocation.x -= relativeDragLocation.x;
newLocation.y -= relativeDragLocation.y;
draggedTable.moveTo(newLocation);
repaint(oldLocation.x, oldLocation.y, width, height);
repaint(newLocation.x, newLocation.y, width, height);
event.consume();
}
super.processMouseMotionEvent(event);
}
public void addTable(TableType type) {
Table table = new Table(type);
// Place in center of panel, initially.
table.moveTo(new Point(getWidth() / 2, getHeight() / 2));
tables.add(table);
repaint();
}
static void showInWindow() {
TableArranger arranger = new TableArranger();
JMenuItem addRed = new JMenuItem("Add Red");
addRed.addActionListener(e -> arranger.addTable(TableType.RED));
JMenuItem addBlue = new JMenuItem("Add Blue");
addBlue.addActionListener(e -> arranger.addTable(TableType.BLUE));
JMenu menu = new JMenu("Table");
menu.add(addRed);
menu.add(addBlue);
JMenuBar menuBar = new JMenuBar();
menuBar.add(menu);
JFrame frame = new JFrame("Table Arranger");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setJMenuBar(menuBar);
frame.getContentPane().add(arranger);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> showInWindow());
}
}