在JLabel中将标记放在ImageIcon上

所以我试图找到一种方法来修改Java中的图像。 换句话说,如果用户点击图像,则标记将放在用户刚刚点击的位置。 我有一个ImageIcon,我把它放在JLabel中。 到目前为止,我采用的方法是使用JLayeredPanel将另一个JPanel放在JLabel之上并在此JPanel上绘制:

//... ImageIcon icon = new ImageIcon("foo.jpg"); JLabel lb = new JLabel(icon); JPanel glass = new JPanel(); lb.setBounds(0, 0, 100, 100); glass.setBounds(0, 0, 100, 100); glass.setOpaque(false); LayeredPane container = new LayeredPane(); container.add(lb, 1); container.add(glass, 2); //... 

但这种方式似乎不起作用。 我从未看到背景图像(lb中的图像)。 所以我想知道我是否在正确的轨道上? 或者有更清洁的方法来实现这一目标吗?

使用JLayeredPane或玻璃窗格对于这样的事情没有任何问题,个人而言,我发现它很麻烦,因为在大型应用程序中,您倾向于将这些层用于任何数量的事情,因此它变得非常复杂非常快。

我更喜欢把它保留在“家庭中”,可以这么说……

就个人而言,我会使用自定义组件。 这将工作流程隔离到一个非常特定的位置,使您更容易提供您可能喜欢的自定义…

在此处输入图像描述

 public class MarkImage { public static void main(String[] args) { new MarkImage(); } public MarkImage() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { private BufferedImage background; private List clickPoints; public TestPane() { clickPoints = new ArrayList<>(25); try { background = ImageIO.read(getClass().getResource("/Miho_Small.png")); } catch (IOException ex) { ex.printStackTrace(); } addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { clickPoints.add(e.getPoint()); repaint(); } }); } @Override public Dimension getPreferredSize() { return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight()); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (background != null) { int x = (getWidth() - background.getWidth()) / 2; int y = (getHeight() - background.getHeight()) / 2; g.drawImage(background, x, y, this); } g.setColor(Color.RED); for (Point p : clickPoints) { g.fillOval(px - 4, py - 4, 8, 8); } } } } 

我还考虑使用JXLayer (Java 7中的AKA JLayer )。 这最好描述为组件的玻璃板(类固醇)。 查看如何装饰组件以获取更多详细信息……

更新了JLayer示例

这是使用Java 7的JLayer一个例子。 JLayerJXLayer之间存在一些细微差别,但转换它并不需要太多…

(对不起,忍不住以前的诱惑)

在此处输入图像描述

 public class MarkLayer { public static void main(String[] args) { new MarkLayer(); } public MarkLayer() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } try { JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new GridBagLayout()); JLabel label = new JLabel(new ImageIcon(ImageIO.read(getClass().getResource("/Miho_Small.png")))); LayerUI layerUI = new MarkLayerUI(); JLayer layer = new JLayer<>(label, layerUI); frame.add(layer); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } catch (Exception exp) { exp.printStackTrace(); } } }); } public class MarkLayerUI extends LayerUI { private Map> mapPoints; public MarkLayerUI() { mapPoints = new WeakHashMap<>(25); } @Override public void installUI(JComponent c) { System.out.println("install"); super.installUI(c); JLayer layer = (JLayer) c; layer.setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK); } @Override public void uninstallUI(JComponent c) { super.uninstallUI(c); mapPoints.remove((JLayer) c); } @Override protected void processMouseEvent(MouseEvent e, JLayer l) { if (e.getID() == MouseEvent.MOUSE_CLICKED) { List points = mapPoints.get(l); if (points == null) { points = new ArrayList<>(25); mapPoints.put(l, points); } Point p = e.getPoint(); p = SwingUtilities.convertPoint(e.getComponent(), p, l); points.add(p); l.repaint(); } } @Override public void paint(Graphics g, JComponent c) { Graphics2D g2d = (Graphics2D) g.create(); super.paint(g2d, c); g2d.setColor(Color.BLUE); g2d.drawRect(0, 0, c.getWidth() - 1, c.getHeight() - 1); List points = mapPoints.get((JLayer) c); if (points != null && points.size() > 0) { g2d.setColor(Color.RED); for (Point p : points) { g2d.fillOval(px - 4, py - 4, 8, 8); } } g2d.dispose(); } } } 

蓝色边框是渲染器,作为图层的一部分,这为您提供了可以单击的位置的指南 – 我这样做是为了测试和演示目的

你想要使用另一个窗格,你是在正确的轨道上。 在Java中,实际上已经有一个专为此目的而设计的玻璃窗格。 阅读本教程http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html ,它应该有助于您理解。