如何将MouseListener添加到Java Swing Canvas上的项目

Sar*_*all 1 java swing position canvas

我想创建一个Java面板,用于创建用户单击的对象.由于我的实际应用程序使用MVC方法,我也希望这些对象能够在模型更改时重新绘制自己,并提供更改其属性的菜单.

我认为控制x和y位置的最佳方法是采用基于画布的方法,从方法中JPanel调用这些对象的draw paintComponent方法.然而,这只会在画布上绘制形状,并且不会添加对象本身而失去控制对象属性的所有能力.如果有人能告诉我最适合自己想做的事情,我将非常感激.

我已经创建了一些示例代码,可以在下面看到.单击时,我希望圆圈改变颜色,这是使用MouseListener实现的(它基本上代表在这个小例子中更改模型属性).此外,我还想确保放大/缩小仍然适用于任何示例代码/建议可以提供,所以我添加了按钮来放大和缩小对象作为快速测试.

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.geom.Ellipse2D;

public class Main  {

    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                ExamplePanel panel = new ExamplePanel();

                frame.add(panel);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    //I could not get this to with when it extended JLayeredPane
    private static class ExamplePanel extends JPanel {
        private static final int maxX = 500;
        private static final int maxY = 500;
        private static double zoom = 1;
        private static final Circle circle = new Circle(100, 100);

        public ExamplePanel() {
            this.setPreferredSize(new Dimension(maxX, maxY));
            this.setFocusable(true);

            Button zoomIn = new Button("Zoom In");
            zoomIn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    zoom += 0.1;
                    repaint();
                }
            });
            add(zoomIn);

            Button zoomOut = new Button("Zoom Out");
            zoomOut.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    zoom -= 0.1;
                    repaint();
                }
            });
            add(zoomOut);


            // add(circle); // Comment back in if using JLayeredPane
        }

        @Override
        public void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.scale(zoom, zoom);
            super.paintComponent(g);

            circle.paint(g); // Comment out if using JLayeredPane
        }

    }

    static class Circle extends JPanel {
        private Color color = Color.RED;
        private final int x;
        private final int y;
        private static final int DIMENSION = 100;

        public Circle(int x, int y) {
            // setBounds(x, y, DIMENSION, DIMENSION);
            this.x = x;
            this.y = y;
            addMouseListener(new MouseAdapter() {

                @Override
                public void mousePressed(MouseEvent e) {
                    color = Color.BLUE;
                }

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

        public void paint(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setPaint(color);
            g2.fillOval(x, y, DIMENSION, DIMENSION); 
        }

        // I had some trouble getting this to work with JLayeredPane even when setting the bounds
        // In the constructor
        // @Override
        // public void paintComponent(Graphics g) {
        //     Graphics2D g2 = (Graphics2D) g;
        //     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        //     g2.setPaint(color);
        //     g2.fillOval(x, y, DIMENSION, DIMENSION); 
        // }

        @Override
        public  Dimension getPreferredSize(){
            return new Dimension(DIMENSION, DIMENSION);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,我确实尝试使用JLayeredPane(有用,因为我也喜欢将我的对象分层),但无法让我的对象甚至渲染.我知道它没有默认的布局管理器所以尝试setBounds在构造函数中调用圆圈,但遗憾的是它没有用.我知道使用布局管理器更好,但似乎找不到适合我需求的布局管理器!

提前致谢.

Mad*_*mer 7

不要覆盖paint组件,使用paintComponent,不要忘记打电话super.paintComponent

组件已经具有"位置"的概念,因此在绘制时,组件的左上角位置实际上是 0x0

你正在做的事实上是超越你组件的边界

例如,如果你把你Circle100x100,然后做...

g2.fillOval(x, y, DIMENSION, DIMENSION); 
Run Code Online (Sandbox Code Playgroud)

您实际上将开始绘画200x200(100表示​​组件的实际位置,100表示​​额外的定位).

而是使用

g2.fillOval(x, y, DIMENSION, DIMENSION); 
Run Code Online (Sandbox Code Playgroud)

然后回去尝试使用JLayeredPane.

您实际上可以编写自己的布局管理器,它获取组件的位置和它的首选大小并更新组件边界,然后将其应用于JLayeredPane.这为您提供了绝对布局的"好处",但让您了解Swing如何在事情发生变化时更新其组件.

你应该小心做任何事情......

Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Run Code Online (Sandbox Code Playgroud)

所述Graphics上下文是一个共享的资源.这意味着,当您绘制下一个组件时,您应用的任何内容仍然有效.这可能会产生一些奇怪的结果.

而是尝试使用......

Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//...
g2.dispose();
Run Code Online (Sandbox Code Playgroud)

更新

对于缩放,我会仔细看看JXLayer(或Java 7中的JLayer)

JXLayer(以及优秀的PBar扩展程序)已经上网了,所以你可以从这里获取一份副本

(我试图找到一个更好的例子,但这是我能用有限时间做的最好的事情)

在此输入图像描述

更新了工作缩放示例

ZoomZoom

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;

public class TestJLayerZoom {

    public static void main(String[] args) {
        new TestJLayerZoom();
    }

    public TestJLayerZoom() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                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 JXLayer<JComponent> layer;
        private DefaultTransformModel transformModel;
        private JPanel content;

        public TestPane() {

            content = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridy = 0;

            JLabel label = new JLabel("Hello");
            JTextField field = new JTextField("World", 20);

            content.add(label, gbc);
            content.add(field, gbc);

            gbc.gridy++;
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            final JSlider slider = new JSlider(50, 200);
            slider.addChangeListener(new ChangeListener() {

                @Override
                public void stateChanged(ChangeEvent e) {
                    int value = slider.getValue();
                    double scale = value / 100d;
                    transformModel.setScale(scale);
                }
            });
            content.add(slider, gbc);

            transformModel = new DefaultTransformModel();
            transformModel.setScaleToPreferredSize(true);

            Map<RenderingHints.Key, Object> hints = new HashMap<>();
            //hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            //hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            //hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

            layer = TransformUtils.createTransformJXLayer(content, transformModel, hints);
            setLayout(new BorderLayout());
            add(layer);


        }

    }

}
Run Code Online (Sandbox Code Playgroud)

我已经留下了渲染提示来演示它们的用法,但是我发现它们在文本字段中用光标的位置搞定了,但是你可能想玩一个游戏