Java JButton子类在重新绘制期间具有自定义形状移动

Java JButton子类在重新绘制期间具有自定义形状移动,java,swing,jbutton,custom-component,Java,Swing,Jbutton,Custom Component,我刚刚开始使用Swing,正在尝试绘制一个具有自定义形状的按钮,本例中为三角形。我在下面的代码中调用了JButton子类“ShiftingButton”,因为它的异常行为。当鼠标进入其区域时,将使用其原始位置的偏移重新绘制鼠标。此外,除了原始位置之外,还会绘制偏移版本,以便原始版本和偏移版本同时显示。也就是说,当我运行此代码时,按钮显示为沿窗口左边缘的三角形。然后,当我将鼠标移到按钮上时,会绘制一个新的三角形(除了旧的三角形之外),向下和向右移动大约10个像素。调整窗口大小会更改幻影按钮相对于原

我刚刚开始使用Swing,正在尝试绘制一个具有自定义形状的按钮,本例中为三角形。我在下面的代码中调用了JButton子类“ShiftingButton”,因为它的异常行为。当鼠标进入其区域时,将使用其原始位置的偏移重新绘制鼠标。此外,除了原始位置之外,还会绘制偏移版本,以便原始版本和偏移版本同时显示。也就是说,当我运行此代码时,按钮显示为沿窗口左边缘的三角形。然后,当我将鼠标移到按钮上时,会绘制一个新的三角形(除了旧的三角形之外),向下和向右移动大约10个像素。调整窗口大小会更改幻影按钮相对于原始按钮的偏移

对鼠标点击的实验表明,只有原始的、正确定位的按钮处于活动状态。“偏移幻影”按钮的区域未激活

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

public class ShiftingButton extends JButton implements ActionListener {
    private Polygon shape;
    public ShiftingButton () {
        initialize();
        addActionListener(this);
    }
    protected void initialize() {
        shape = new Polygon();
        setSize(120, 120);
        shape.addPoint(0, 0);
        shape.addPoint(0, 60); 
        shape.addPoint(90, 0);
        setMinimumSize(getSize());
        setMaximumSize(getSize());
        setPreferredSize(getSize());
    }
    // Hit detection
    public boolean contains(int x, int y) {
        return shape.contains(x, y);
    }
    @Override
    public void paintComponent (Graphics g) {
        System.err.println("paintComponent()");
        g.fillPolygon(shape);
    }
    protected void paintBorder(Graphics g) {
    }
    @Override
    public void actionPerformed (ActionEvent ev) {
        System.out.println("ShiftingButton ActionEvent!");
    }
    public static void main (String[] args) {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        ShiftingButton button = new ShiftingButton();
        panel.add(button);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);
    }
}

在被重写的
paintComponent(…)
方法内调用
super.paintComponent(g)
失败。此外,在重写
Base
类的方法时,始终尽量保持方法的访问说明符相同。在这种情况下,它是受保护的,而不是公共的,现在函数应该是这样的:

@Override
protected void paintComponent (Graphics g) {
    System.err.println("paintComponent()");
    super.paintComponent(g);
    g.fillPolygon(shape);
}
编辑1: 此外,由于您使用的是要绘制的自定义形状,因此您再次未能为所讨论的
JButton
指定
ContentAreaFilled
属性,因此在构造函数中,您应该编写,以使其正常工作。虽然如果这不起作用(出于文档中指定的原因),那么您必须使用普通的旧
不透明
属性,并使用
设置不透明(假)
:-)将其设置为

以下是修改后的代码:

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

public class ShiftingButton extends JButton implements ActionListener {

    private Polygon shape;

    public ShiftingButton () {
        setContentAreaFilled(false);
        initialize();
        addActionListener(this);
    }

    protected void initialize() {
        shape = new Polygon();
        setSize(120, 120);
        shape.addPoint(0, 0);
        shape.addPoint(0, 60); 
        shape.addPoint(90, 0);
    }

    @Override
    public Dimension getPreferredSize() {
        return (new Dimension(120, 120));
    }

    // Hit detection
    public boolean contains(int x, int y) {
        return shape.contains(x, y);
    }

    @Override
    protected void paintComponent(Graphics g) {
        System.err.println("paintComponent()");
        super.paintComponent(g);
        g.fillPolygon(shape);       
    }

    protected void paintBorder(Graphics g) {
    }

    @Override
    public void actionPerformed (ActionEvent ev) {
        System.out.println("ShiftingButton ActionEvent!");
    }

    public static void main (String[] args) {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        ShiftingButton button = new ShiftingButton();
        panel.add(button);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);
    }
}

@Jorocco:不需要调用
setSize(120120)
即使在构造函数内部,我只是在您的代码中发现了这一点,我删除了其他语句,尽管在代码中遗漏了这一条。因为被重写的
getPreferredSize()
也会处理这个问题:-)谢谢!这就解决了。不过,我还有一个后续问题。虽然调用super.paintComponent()似乎是一个好的OO策略,但我不确定它的实际意义。也就是说,如果我的自定义形状的按钮准确地定义了按钮的外观,而与它的超类完全不同,那么它为什么要依赖JButton的绘制机制呢?如果这听起来有点吹毛求疵,很抱歉,但我只想理解每一行代码。当我在没有super.painComponent()调用的情况下运行它时,我没有看到任何区别。也许它需要更精细的子类化?@Jorocco:请参考这个,这个人用一种非常好的方式解释了这一点。我希望有帮助。对于其他人,我们非常欢迎你并保持微笑:-)