如何将 MouseListener 添加到 Java Swing Canvas 上的项目 [英] How to add MouseListener to item on Java Swing Canvas

查看:26
本文介绍了如何将 MouseListener 添加到 Java Swing Canvas 上的项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'd like to make a Java panel that creates objects where the user clicks. Since my actual application uses a MVC approach I'd like also for these objects to be able to repaint themselves when a model is changed, and provide menus to change their properties.

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

I think that the best way to control their x and y locations would be to take a canvas based approach whereby the JPanel calls a draw method on these objects from the paintComponent method. This however will only draw the shape on the canvas and does not add the object itself loosing all abilities to control object properties. I'd be very grateful if someone could tell me the best approach for what I want to do.

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

I've created some sample code which can be seen below. When clicked I'd like the circle to change colour, which is implemented using a MouseListener (it basically represents changing the models properties in this small example). Also I'd just like to make sure that zooming in/out still works with any sample code/advice can provide so I've added buttons to zoom the objects in and out as a quick test.

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);
        }
    }
}

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

As an aside I did try using a JLayeredPane(useful because I'd also like to layer my objects) but could not get my objects to even render. I know it has no default layout manager so tried calling setBounds in the circle in the constructor, but sadly it did not work. I know it's better to use a layout manager but can't seem to find one suitable for my needs!

提前致谢.

推荐答案

不要覆盖 paint 组件,使用 paintComponent 并且不要忘记调用 super.paintComponent

Don't override paint components, use paintComponent and don't forget to call super.paintComponent

一个组件已经有了位置"的概念,所以在绘制的时候,你的组件的左上角位置实际上是0x0

A component already has a concept of "location", so when painting, the top left position of your component is actually 0x0

你所做的实际上是超出了你的组件边界

What you are doing is actually painting beyond the boundaries of you component

例如,如果您将 Circle 放在 100x100 处,然后...

For example, if you place your Circle at 100x100 and then did...

g2.fillOval(x, y, DIMENSION, DIMENSION); 

您实际上会从 200x200 开始绘制(100 表示组件的实际位置,100 表示附加定位).

You would actually start painting at 200x200 (100 for the actual location of the component and 100 for you additional positioning).

改为使用

g2.fillOval(x, y, DIMENSION, DIMENSION); 

然后返回并尝试使用 JLayeredPane.

And go back and try using JLayeredPane.

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

You could actually write your own layout manager that takes the location of the component and it's preferred size and updates the components bounds and then apply this to a JLayeredPane. This gives you the "benefits" of an absolute layout, but keeps you within how Swing works to update its components when things change.

你在做任何事情时也应该小心......

You should also be careful with doing anything like...

Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

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

The Graphics context is a shared resource. That means, anything you apply to, will still be in effect when the next component is painted. This may produce some strange results.

而是尝试使用...

Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//...
g2.dispose();

更新

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

For zooming I would take a closer look at JXLayer (or JLayer in Java 7)

JXLayer(和优秀的 PBar 扩展)已经在网上流行了,所以你可以从 这里

The JXLayer (and excellent PBar extensions) have gone quite on the net, so you can grab a copy from here

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

(I tried finding a better example, but this is the best I could do with the limited time I have available)

更新了工作缩放示例

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);


        }

    }

}

我已经留下了渲染提示来演示它们的使用,但我发现它们在文本字段中定位光标的问题,但您可能想玩玩

I've left the rendering hints in to demonstrate their use, but I found that they screwed with the positing of the cursor within the text field, but you might like to have a play

这篇关于如何将 MouseListener 添加到 Java Swing Canvas 上的项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆