自定义Java鼠标监听器?

5

首先,我作为一名网站程序员转向Java。在JavaScript中,添加mousemove、mouseover或click事件的方法只需要调用addEventListener函数即可。但是根据我有限的Java经验,你不能随便实现任何对象的MouseListener接口。

基本上,我现在有一个JPanel,可以绘制一些具有x/y/width/height值的形状(使用paint方法的CustomShape对象)。我想为形状对象添加某种类型的鼠标监听器,以便我可以为形状触发移动/滚动/单击事件。仅仅将MouseListener接口实现到CustomShape对象并不能起作用(这个显然很容易理解)。我已经查阅了如何设计自定义事件监听器,但似乎不可能创建自定义鼠标监听器。

最终,我决定将鼠标监听器添加到JPanel中,并循环遍历所有形状对象。如果形状对象附加了'listener',并且鼠标坐标验证了鼠标事件的发生,它就会触发该方法。最初,这是可以的,但随着应用程序的发展,它变得非常混乱。此外,我将无法将形状对象/接口复制到另一个应用程序中而不复制大量代码。

以下是简单的示例:(实际代码非常庞大)

Interface CustomShape{
    int width, height, x, y;
    void paint(Graphics g);
}

public class StarShape implements CustomShape{
    int width, height, x, y;
    public StarShape(){
        width = 100;
        height = 100;
        x = 50;
        y = 50;
    }
    void paint(Graphics g){
        g.setColor(Color.black);
        g.draw(new Rectangle(x,y,width,height));
    }
}

public class Main extends JPanel{
    StarShape check = new StarShape();
    public Main(){  }
    @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        check.paint(g);
    }
}

那么,我想知道是否有一种清晰的方法来为“手绘”形状实现某种类型的鼠标监听器。

3个回答

5
为了能够接收事件,您的“Shape”对象应该扩展java.awt.Component(或javax.swing.JComponent)。然后将它们作为子元素添加到JPanel中,它们就会接收事件,您可以直接向它们添加监听器。
按照您目前的方式,您需要手动跟踪JPanel中形状的位置。您需要将鼠标监听器添加到面板本身,并根据所得到的事件的x/y坐标调用某些形状上的方法来处理事件。这基本上是重新实现了基本的AWT/Swing类为您完成的操作。

据我了解,Java的内部GUI组件会优化渲染。因此,根据组件的透明度、重叠、焦点、鼠标位置或位置,Java会优化以仅重新呈现必要的部分。如果我使用自己的形状对象扩展组件对象,那么Java会忽略所有这些渲染优化吗?我认为会,因为我将覆盖paintComponent类。 - Azmisov

2

我对你正在尝试做的事情的方法是:编译,运行,阅读 :-)

(注意:您可以将下面每一行代码复制到一个文件Example01.java中。编译并运行它。)

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

public class Example01 {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame jf = new JFrame();
                MainPanel mainPanel = new MainPanel();
                jf.add(mainPanel);
                jf.setSize(640, 480);
                jf.setLocationRelativeTo(null);
                jf.setVisible(true);
            }
        });
    }
}

class MainPanel extends JPanel {
    StarShape check1 = new StarShape();
    StarShape check2 = new StarShape();

    public MainPanel() {
        check1.setName("check1");
        check2.setName("check2");
        check1.addMouseListener(new MyMouseListener(check1));
        check2.addMouseListener(new MyMouseListener(check2));
        this.add(check1);
        this.add(check2);
    }
}

class StarShape extends JComponent {
    public StarShape() {
        this.setPreferredSize(new Dimension(100, 100));
    }

    @Override
    protected void paintComponent(Graphics g) {
        g.setColor(Color.black);
        Graphics2D g2d = (Graphics2D) g;
        int x = 0;
        int y = 0;
        int width = this.getWidth() - 1;
        int height = this.getHeight() - 1;
        g2d.draw(new Rectangle(x, y, width, height));
    }
}

class MyMouseListener implements MouseListener {
    private final JComponent component;

    public MyMouseListener(JComponent component) {
        this.component = component;
    }

    public void mouseClicked(MouseEvent e) {
        System.out.println("mouseClicked: " + component.getName());
    }

    public void mouseEntered(MouseEvent e) {
        System.out.println("mouseEntered: " + component.getName());
        Dimension preferredSize = component.getPreferredSize();
        preferredSize.height += 20;
        preferredSize.width += 20;
        component.setPreferredSize(preferredSize);
        component.invalidate();
        SwingUtilities.getWindowAncestor(component).validate();
    }

    public void mouseExited(MouseEvent e) {
        System.out.println("mouseExited: " + component.getName());
        Dimension preferredSize = component.getPreferredSize();
        preferredSize.height -= 20;
        preferredSize.width -= 20;
        component.setPreferredSize(preferredSize);
        component.invalidate();
        SwingUtilities.getWindowAncestor(component).validate();
    }

    public void mousePressed(MouseEvent e) {
        System.out.println("mousePressed: " + component.getName());
    }

    public void mouseReleased(MouseEvent e) {
        System.out.println("mouseReleased: " + component.getName());
    }
}

谢谢您提供的示例。那非常有帮助和说明性。 - Azmisov

1
你可以让这个形状继承 JPanel
public abstract class CustomShape extends JPanel {

    public CustomShape(){
        setOpaque(false);
    }

    public abstract void paintShape(Graphics g);

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        paintShape(g);
    }

}

然后,您可以直接在形状上添加监听器。

接下来,您需要创建父级JPanel,并将其LayoutManager设置为null。然后,您需要手动设置形状在父级上的位置。

这里阅读更多关于手动布局组件的内容。如果您不喜欢这种方法,请查看我的答案这个问题。它讲述了如何使用鼠标在屏幕上移动彩色矩形。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接