菜单栏上图像图标位置重置点击

问题描述 投票:0回答:1

我正在尝试将图像图标拖放到屏幕上,但每当我在移动图标后单击菜单栏时,它都会重置到原来的位置。我认为这与 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 方法一样,它对对象做了同样的事情。

java swing drag-and-drop jpanel jmenubar
1个回答
0
投票

Screen 对象的 LayoutManager(恰好是 FlowLayout,因为这是默认布局)决定子组件的放置位置以及它们的大小。 此过程在验证面板时发生,也可能因其他原因而发生。 每次发生时,您的

component.setLocation(location);
调用的效果都会被丢弃。

应用程序不应显式设置组件的位置(或大小)。 获取组件的尺寸,即使是像标签这样简单的东西,也可能是一个复杂的过程。 文本是根据字体的磅值和显示器的点距呈现的。 图像可以使用系统的 HiDPI 设置进行缩放。

所有这些都是由 Swing 处理的。 组件能够报告自己的首选尺寸,布局管理器将适应这些尺寸,并根据布局管理器的合同放置东西。 (例如,BorderLayout 可以在中心放置一个组件,在两侧放置一个组件。)

但是你不希望东西自动放置; 您希望它们显示在用户拖动它们的任何位置。 这意味着,您的需求与所有 LayoutManager 直接冲突。

因此,您不应该使用子组件来显示图标。 相反,直接在 JPanel 上绘制它们。

这需要一些东西:

  1. 保存数据模型的字段。 这只是面板将绘制的表数据对象的列表,而不是组件。
  2. 包含当前正在拖动的项目的字段,因此鼠标移动事件知道要移动哪个项目。

这是一个例子:

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());
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.