有没有办法在 JLabel 上设置锚点?

Is there a way to set an anchor on a JLabel?

我希望能够在中心为 JLabel 设置一个锚点,这样我就可以以此为基础移动它。

如果您试图使 JLabel 围绕某个点居中,那么您只需要简单的数学运算即可,仅此而已。

假设您有一个 JLabel 或我们将命名为 "component" 的任何组件,它保存在 JPanel 或我们将称为 "container" 的任何容器中,并假设屏幕上有一个鼠标点 ,假设称为 "mousePoint",那么数学很简单:

Point mousePoint = e.getLocationOnScreen();
Point containerLocation = container.getLocationOnScreen();
Dimension componentSize = component.getSize();

int x = mousePoint.x - componentSize.width / 2 - containerLocation.x;
int y = mousePoint.y - componentSize.height / 2 - containerLocation.y;
component.setLocation(x, y);

就是这样。

例如,假设您有两个 JLabel,一个带有图像,一个带有一些文本,那么您可以向两者添加相同的 MouseListener 和 MouseMotionListener,这将允许您通过它们的任意一个进行拖动中心点。这是我在上面的评论中提到的 MCVE 的示例:

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.*;

public class ClickDragLabel extends JPanel {
    public static final String IMG_PATH = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/"
            + "Theodore_Comnenus-Ducas_cropped.jpg/133px-Theodore_Comnenus-Ducas_cropped.jpg";
    private static final int PREF_W = 1000;
    private static final int PREF_H = 850;
    private JLabel imageLabel;
    private JLabel textLabel = new JLabel("Some Random Text");

    public ClickDragLabel(Icon icon) {
        setBackground(Color.WHITE);
        imageLabel = new JLabel(icon);

        setLayout(null);
        imageLabel.setSize(imageLabel.getPreferredSize());
        textLabel.setSize(textLabel.getPreferredSize());

        imageLabel.setLocation(250, 250);
        textLabel.setLocation(10, 10);

        MyMouse myMouse = new MyMouse();
        imageLabel.addMouseListener(myMouse);
        imageLabel.addMouseMotionListener(myMouse);
        textLabel.addMouseListener(myMouse);
        textLabel.addMouseMotionListener(myMouse);

        add(imageLabel);
        add(textLabel);

    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        return new Dimension(PREF_W, PREF_H);
    }

    private class MyMouse extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            center(e);
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            center(e);
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            center(e);
        }

        private void center(MouseEvent e) {
            JComponent component = (JComponent) e.getSource();
            Container container = component.getParent();

            Point mousePoint = e.getLocationOnScreen();
            Point containerLocation = container.getLocationOnScreen();
            Dimension componentSize = component.getSize();

            int x = mousePoint.x - componentSize.width / 2 - containerLocation.x;
            int y = mousePoint.y - componentSize.height / 2 - containerLocation.y;
            component.setLocation(x, y);
            container.repaint();
        }

    }

    private static void createAndShowGui() {
        Image img = null;
        try {
            URL imgUrl = new URL(IMG_PATH);
            img = ImageIO.read(imgUrl);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(-1);
        }

        Icon icon = new ImageIcon(img);

        ClickDragLabel mainPanel = new ClickDragLabel(icon);

        JFrame frame = new JFrame("Click-Drag Label");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

发布此文后,如果我不提及使用空布局的弊端,那将是我的失职。虽然空布局和 setBounds() 对于 Swing 新手来说似乎是创建复杂 GUI 的最简单和最好的方法,但您创建的 Swing GUI 越多,您在使用它们时 运行 就会遇到更严重的困难。它们不会在 GUI 调整大小时调整组件的大小,它们是皇家 来增强或维护的,当放置在滚动窗格中时它们会完全失败,当在所有平台或不同的屏幕分辨率上查看时它们看起来很糟糕从原来的。我只将它们用于动画,如上图,除此之外别无他用。

Is there a way to set an anchor on a JLabel?

您似乎想要的可以通过设置和更改标签的 EmptyBorder 来实现。

import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;

public class MovableLabel {

    private JComponent ui = null;
    String anchorString = new String(Character.toChars(9875));
    private JLabel label = new JLabel(anchorString);
    int pad = 200;
    SpinnerNumberModel xModel = new SpinnerNumberModel(0, 0, pad, 1);
    SpinnerNumberModel yModel = new SpinnerNumberModel(0, 0, pad, 1);

    MovableLabel() {
        initUI();
    }

    public void initUI() {
        if (ui != null) {
            return;
        }

        ui = new JPanel(new BorderLayout(4, 4));
        ui.setBorder(new EmptyBorder(4, 4, 4, 4));

        Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
        Font font = null;
        for (Font f : fonts) {
            if (f.canDisplayUpTo(anchorString) < 0) {
                font = f.deriveFont(40f);
                break;
            }
        }
        label.setFont(font);
        label.setBackground(Color.BLUE);
        setBorder();
        ui.add(label);

        JToolBar tb = new JToolBar();
        ui.add(tb, BorderLayout.PAGE_START);

        ChangeListener changeListener = new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                setBorder();
            }
        };

        tb.add(new JLabel("X"));
        JSpinner xSpinner = new JSpinner(xModel);
        xSpinner.addChangeListener(changeListener);
        tb.add(xSpinner);

        tb.add(new JLabel("Y"));
        JSpinner ySpinner = new JSpinner(yModel);
        ySpinner.addChangeListener(changeListener);
        tb.add(ySpinner);
    }

    private void setBorder() {
        int x = xModel.getNumber().intValue();
        int y = yModel.getNumber().intValue();
        EmptyBorder border = new EmptyBorder(x, y, pad - x, pad - y);
        label.setBorder(border);
    }

    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception useDefault) {
                }
                MovableLabel o = new MovableLabel();

                JFrame f = new JFrame(o.getClass().getSimpleName());
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                f.setLocationByPlatform(true);

                f.setContentPane(o.getUI());
                f.pack();
                f.setMinimumSize(f.getSize());

                f.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(r);
    }
}