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

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

问题描述

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



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



我创建了一些示例代码,如下所示。当点击我想让圆更改颜色,这是实现使用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);
}
});
}

//当扩展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();
} $ b b});
add(zoomIn);

按钮zoomOut =新按钮(缩小);
zoomOut.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
zoom - = 0.1;
repaint();
}
});
add(zoomOut);


// add(circle); //如果使用JLayeredPane,则返回注释。


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

circle.paint(g); //注释掉如果使用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);
}

//我在使用JLayeredPane时遇到了一些麻烦,即使设置边界
//在构造函数中
// @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 在构造函数中的圆圈,但可惜的是它没有工作。我知道最好使用布局管理器,但似乎找不到一个适合我的需要!



提前感谢。

解决方案

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



组件已经有一个位置所以当绘画时,你的组件的左上角位置实际上是 0x0



您组件的边界



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

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

你实际上开始在 200x200



而是使用

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

然后返回尝试使用 JLayeredPane



你实际上可以编写自己的布局管理器,它接受组件的位置和它的首选大小,并更新组件边界,然后将其应用到。这给你绝对布局的好处,但保持你在Swing的工作原理更新其组件时,改变。



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

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

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



而是尝试使用...

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

更新



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



JXLayer(和优秀的PBar扩展)已经在网上,因此,您可以从此处获取副本



(我试过找一个更好的例子,但这是我能用有限的时间我可以做到的最好的)。



< img src =https://i.stack.imgur.com/4Jw38.pngalt =enter image description here>



已更新缩放示例





  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>层;
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(labels,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'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.

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.

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

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!

Thanks in advance.

解决方案

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

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

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

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

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

Instead use

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

And go back and try using JLayeredPane.

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

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.

Instead try using...

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

Updated

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

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)

Updated with working zooming example

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天全站免登陆