You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

继承JButton自定义绘制后,如何限制ActionListener仅在按钮区域触发?

问题描述

我尝试在JFrame上绘制Windows 11风格按钮,绘制效果良好,但无法正确为其添加ActionListener。以下是我的代码:

public class MyButton extends JButton implements MouseListener, ActionListener {
    public MyButton(String text) {
        //super(label);
        enableInputMethods(true);
        addMouseListener(this);

        setFocusPainted(false);
        setBorderPainted(false);

        buttonText = text;
    }

    @Override
    protected void paintComponent(Graphics g) {
        //super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;
        FontMetrics metrics = g2.getFontMetrics(Constants.arialFont);
        g2.setStroke(new BasicStroke(2.0f));
        g2.setFont(Constants.arialFont);

        g2.setPaint(Constants.buttonNormal);
        rect = new RoundRectangle2D.Double(0, 0, metrics.stringWidth(buttonText) + 20, 27, 10, 10);
        g2.fill(rect);

        g2.setPaint(Constants.fontColor);
        g2.drawString(buttonText, Functions.getMiddleFromX(g2, rect, buttonText), Functions.getMiddleFromY(g2, rect, buttonText));
    }

我尝试按如下方式添加MouseListener:

@Override
    public void mouseClicked(MouseEvent e) {
        // Check if the click is on the button.
        Point p = e.getPoint();
        if(rect.contains(p)) System.out.println("Triangle contains point");
        else { return; }
    }

但运行以下代码时,点击JFrame任意区域都会打印"Action Performed!":

MyButton myButton = new MyButton("Epic button!");
        myButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Action Performed!");
            }
        });
        frame.add(myButton);

请问如何过滤事件,仅在点击RoundRectangle2D.Double区域时执行操作?

解决方案

问题核心是JButton默认以矩形作为可点击区域,哪怕你绘制了圆角样式,组件依然会响应整个矩形范围内的点击,甚至组件大小不匹配时,空白区域也会触发事件。按以下步骤修复:

  • 匹配组件尺寸与圆角矩形
    在构造方法中根据文本计算按钮尺寸,设置组件的固定大小,让组件边界和绘制的圆角区域一致:

    public MyButton(String text) {
        enableInputMethods(true);
        setFocusPainted(false);
        setBorderPainted(false);
    
        buttonText = text;
        // 计算并设置按钮尺寸
        FontMetrics metrics = getFontMetrics(Constants.arialFont);
        int btnWidth = metrics.stringWidth(buttonText) + 20;
        int btnHeight = 27;
        setPreferredSize(new Dimension(btnWidth, btnHeight));
        setMinimumSize(new Dimension(btnWidth, btnHeight));
        setMaximumSize(new Dimension(btnWidth, btnHeight));
    }
    
  • 重写contains方法限制点击区域
    JComponent的contains方法决定组件是否识别某个点在自身范围内,重写它来判断点击点是否在你的圆角矩形内:

    private RoundRectangle2D rect;
    
    @Override
    public boolean contains(int x, int y) {
        return rect != null && rect.contains(x, y);
    }
    

    确保rect在paintComponent中正确初始化,这个方法才能正常工作。

  • 移除自定义MouseListener,使用原生事件
    重写contains后,JButton的原生事件机制会自动过滤圆角外的点击,直接使用addActionListener即可,无需自己编写MouseListener判断逻辑。

  • 恢复super.paintComponent(g)调用(推荐)
    注释掉父类的绘制方法可能导致背景、焦点状态等异常,建议保留该调用,再绘制自定义样式:

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        FontMetrics metrics = g2.getFontMetrics(Constants.arialFont);
        g2.setStroke(new BasicStroke(2.0f));
        g2.setFont(Constants.arialFont);
    
        g2.setPaint(Constants.buttonNormal);
        rect = new RoundRectangle2D.Double(0, 0, metrics.stringWidth(buttonText) + 20, 27, 10, 10);
        g2.fill(rect);
    
        g2.setPaint(Constants.fontColor);
        g2.drawString(buttonText, Functions.getMiddleFromX(g2, rect, buttonText), Functions.getMiddleFromY(g2, rect, buttonText));
    }
    

内容的提问来源于stack exchange,提问作者user21603351

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 05:54:59